#[1]gnikyt feed
[2]gnikyt / Code ramblings
[3]Ty King
[4]About[5]Github[6]LinkedIn[7]CV[8]RSS
Building a BASH template engine /
[9]Logo of cmd
Earlier this year I migrated my blog away from Jekyll because it was
such a large setup for such a small blog to maintain. I wanted
something more "portable" without libraries, so I settled to build my
blog and it's generation with BASH.
Everything needed was built out: frontmatter processing, Markdown to
HTML generation, post handling, page handling, category support, RSS
support, sitemap support, and more.
Originally, I wrote a very basic template handler which essentially
just sniffed the first few characters of a string to know an action to
take, example: ${inc:file.html} would include a file into the template,
${var} would replace itself with the applicable variable, and so on. It
began getting more complex where I required some basic if/else
statements and looping, which was something difficult with the basic
template handler I originally wrote.
I decided to rebuild it from scratch, taking inspiration from Mustache
syntax, and ensuring it was more than just a simple find and replace
operation as it was before.
The plan was to do the following:
* Detect the applicable template tags
* For each template tag:
+ Parse it character-by-character to determine:
o It's operation type
o It's optional ask
o It's optional contents
o And more
* Once determined, run the applicable operation to produce a result
* Take the result and replace the original template tag with that
result
Initial supports I targeted:
* Variables: {{VAR}}
* Variables with filters: {{VAR|UPPERCASE|REPLACE x y}}
* If statements: {{#VAR}}I exist!{{/VAR}}
* Unless statements: {{^VAR}}I do not exist!{{/VAR}}
* Partial includes: {{>file.html}}
* Looping: {{@FOREACH var}}{{KEY}}: {{VALUE}}{{/FOREACH var}}
* Assignment/Capture: {{@ASSIGN XYZ}}{{VAR|UPPERCASE}} is
cool!{{/@ASSIGN}}
* Custom functions: {{@MONEY USD}}4500{{/MONEY USD}}
The method I chose, since BASH is limited, was to go
character-by-character through the input.
* If { was the current character, I peek ahead to know if the next
character is also a {
* If the current character and next are both {{, I know this is a
variable or block
* Next, the inside is parsed until }}
* If determined to be a block, then everything after }} until {{/ is
captured
* Once everything is captured, depending on the operation, it would
run specific functions to replace the input contents with the
parsed contents
[10]I built a library to handle this called be.
Example usage, with custom functions and filters:
#!/bin/bash
. ./be
be_bold() {
local input
input=$(cat -)
echo "${input}"
}
be_replace() {
local input
input=$(cat -)
echo "${input//$1/$2}"
}
be_capitalize() {
local input
input=$(cat -)
echo "${input^}"
}
be_equals() {
local block
block=$(cat -)
if [[ "$1" == "$2" ]]; then
be <<< "$block"
else
# Failed, return nothing.
echo ""
fi
}
NAME="Joe"
LIKES="hockey,soccer"
TPL=$(cat <Joey, I heard you like:
1) Hockey (love!); and
2) Soccer
Won't be processed: {{NAME}}
BASH is definately not the best solution for a template engine,
however, you can view the entire solution on [11]here and modify for
your needs.
Anchors
* [1] [12]github.com/gnikyt/be /^
* [2] [13]githuib.com/gnikyt/be/tree/master/be /^
Appendix
Copyright under [14]CC-4.0.
Available in the following alternative formats:
[15]MD / [16]TXT / [17]PDF
*
*
*
*
*
*
*
*
References
1. /rss.xml
2. file:///
3. file:///about
4. file:///about
5. https://github.com/gnikyt
6. https://linkedin.com/in/gnikyt
7. file:///assets/files/cv.pdf
8. file:///rss.xml
9. file:///category/cmd
10. https://github.com/gnikyt/be
11. https://githuib.com/gnikyt/be/tree/master/be
12. https://github.com/gnikyt/be
13. https://githuib.com/gnikyt/be/tree/master/be
14. https://creativecommons.org/licenses/by/4.0/
15. file:///building-bash-template-engine/index.md
16. file:///building-bash-template-engine/index.txt
17. file:///tmp/lynxXXXX2WMOcp/L1551-2466TMP.html