Skip to content
WordBash is a WordPress clone written in GNU Bash
Shell CSS
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


WordPress Clone Written in Bash

Update to the Update

August 2018: The announced update is a... little late. But it shall happen. Just... well, it's "in the queue".

WordBash is less than 2,000 lines of Bash code that works just like WordPress

Update, June 2018 I am going to update this code, specifically the Admin part -- though for security reasons the Admin code runs on Localhost and posts are sent to a site via FTP -- which is um, logical, right? Not having a known admin login page makes for a secure website -- the exact opposite of Wordpress. More later...

Notice, March 2016 I wrote this code in order to learn BASH. And I really enjoyed doing so, having created something indistinguishable from WordPress in 1,200 lines of BASH code. I never got around to releasing the Admin code - sorry for that. People prefer multi-megabyte, massive code full of bells and whistles and social media stuff - if that is you, you do not want this code.

The code has been tested with Linux, OS-X and Windows/Cygwin and Bash version 3.2 and higher. It is a CGI script and has only been tested with Apache.

Please note that this is just a "first release" and lacks many features normally found in WordPress. There are also some things that may look hastily designed, and there are some things that may look unfinished, the result of "reduction" to make this code as small as possible. The Admin code has not been included but I plan to do so.

The code is weird. And I make the claim that it is Object Oriented.


The best way to learn about the code is to install and run it. A knowledge of getting an Apache CGI script to run in a directory is required.


Perhaps the best way to describe the code is to just show the main CGI script. It is really small and does this:

if comment
	add it and redirect for view post

display opening HTML

if view post
	display post
	if error
		display message & exit
	display comments status
	if post has comments
		display comments
	if can add comments
		display comment form
		display message

elseif view page
	display page

	display widgets (sidebar)
	if viewing by tag
		get list of posts by tag
		display tag name/description
	elseif viewing by category
		get list of posts by category
		display category name/description
		get list of posts
	prepare data
	display "navigation" HTML
	display each post in list

display closing HTML

If you are familiar with WordPress you can see how different this is. In WordPress the theme code does that, but WordBash does it all directly in the main code (and true HTML templates are used, detailed in the posts). The process is completely different; the result is the same.

It is backward compared to WordPress, but it is WordPress that is backward. WordBash starts exactly where it should start, at the beginning, displaying data -- WORDBASH.CGI is the theme.


The main source file is called and is "included" by the CGI script. It in turn loads all the "library" code.

All function calls are a single uppercase letter -- yes, I know it is weird and nobody does such a thing (but it makes perfect sense to me).

The function name is indicative of its purpose:

S - server
P - posts
G - pages
W - widgets

The first argument is a "command" and any others are optional data. For example, the line "get list of posts" above refers to line 69 in the code:

P l

That is "Posts list" which creates a list of post IDs in the global array, $Pl.

That is the other weird thing. Each library has it's data as two letter names kind of related to the command name: P l and $Pl, etc. So after the library call each post is displayed (printed) by:

for p in ${Pl[@]}; do
	P p $p

The HTML has been displayed up to that point by lines like:

H e hh
H nv

That is "HTML evaluate template hh" and "HTML print template nv" -- all templates are designated by two letters. Here, hh is for "header" and nv for "navigation". The H library, like some other libraries, has "internal" data and functions that only it uses. The e command is to indicate that the template has variables.

The line:

 S h

was for "Server header" which started the HTML Content data. The code ends with:

 H e ff

for the HTML footer.

The "display comments status" line above (code starting at about line 35) needs a footnote. At 25 lines the code to display comment status and the comment form is not brilliant:

C n gets the number of comments in $Cn, with a result of null meaning comments are not allowed, so it displays one of two HTML templates, ch or cn ("comments header" or "comments none"). Note how variables can be passed to a template.

C p displays the comments.

C + gets whether or not comments can be added. If they can, cf (form) is displayed. If they cannot, cx (max) is displayed, or cc (closed) is.

Two other things here: $rc is non-zero if C a (comment add) had failed -- the $r variables store function exit status -- with ce being comment error template and C e the error string; $_from is, if set, a cookie.

To me, that is a lot of code, and it is even kind of sloppy. But it is fast, easy to understand, easy to modify, extend, adapt, test etc.

It shows the tight integration with the HTML templates, and you will be surprised at just how simple -- and versatile -- the templates are. (The templates are a combination of functions and data in them/wp/

The naming conventions I chose should by now be nearly understood. More details are in the blog posts.


The posts and pages libraries (P and G) both use the database library (D). Posts and pages have the same format, and P and G have similar code. Data is shared between P/G and D. For example, when a post is read (displayed) it's data is in several variables stored in D:

 Dr        the raw data
 Dh        the post headers (array)

And duplicated for use by P (and the HTML templates) as:

 body      the post body
 title     the post title
 date      the post date

Post tags and category are used by P directly as:


And P creates the two variables for the HTML templates:

 tags      an HTML string
 cat       simple string

What this means is that the code is too complicated and can be reduced in size further by eliminating $Dr and $Dh and creating only the others in D.

For use by library W (and the Admin code), lists of tags and categories are in the variables:

 Pt        array of tags
 Pts       tag description string
 Pc        array of category names
 Pcs       category description string

These too are a bit complex and can be simplified.

Library H was designed to not use any other library's data, and some calls to it pass variables. But the templates can also reference any variable (with the e command). This makes for a slight incongruity.

Also, templates can actually be functions which adds flexibility but also complexity (and more weirdness) to the template design.

The other globals are query and post strings. These are read by library Q and an array of allowed names is:

 Qa=(post page tag cat cmt from data cmd arg start)

This means only those strings are read from any GET or POST. All GET strings are sanitized.

Library globals defined in are considered configuration variables that would be modified to adjust the install -- constants. Those declared in the library itself are runtime variables

The variable start -- to generate the "next" and "previous" posts links -- is poorly implemented and incomplete.

The theme directory is them and the one theme is wp.


Every subshell spawns a new version of Bash, with the necessary file I/O and library loading each time -- in a loop this may painfully obvious. Global data, avoided in "real" programming, is a benefit in Bash scripts.

Bash string replacement has a threshold at which performance plummets, jumping from hundreds of milliseconds with a few thousand bytes to seconds with several thousand (depending on the OS and CPU speed). Use of an external program can be a benefit.

However, there is only one place where I found it to be necessary as strings are usually filled with newlines and Bash is very efficient reading strings as lines. The exception is POST data which has no single character with which to split into lines.

I discuss this in post 6 (http://localhost/wordbash/?post=6 or file meta/psts/6).



My tests show that Bash only slows down with string replacement on large strings. But splitting those same strings by newlines makes for fast string replacement (when there are newlines.) See post 6 about the commented out line in the function dt() in source file libs/


In my opinion the code to read a post demonstrates a poor programming practice: in-line strings inter-mixed with code. I usually do not condone such practice. For small code like this though, they are easily managed.

That code is also far too complex. I do not yet see how to reduce this complexity.


The comment code (libs/ is the largest at 340 lines and in some places is a bit complex. I think it would be interesting to provide for the saving of comments to use an external (user supplied) script or program. (Which would be easy to do.)


The meta file hierarchy is not documented yet.


I plan to release the Admin code at some point in the future.


I have purposely not commented the code. I did this as a form of discipline. I learned Bash programming to write this code. And I did not base the code on anyone else's code -- WordBash is entirely of my own making and I used the Bash reference manual as my only programming guide.

(I did, of course, use two other references for a few things: and

You can’t perform that action at this time.