#[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