A Smart, lightweight shell script templating engine, written in Bash.
Bash-TPL lets you mark up textual files (config files, yaml, xml, scripts, html, etc.) with shell commands and variable replacements, while minimally impacting your original file layout.
Templates are compiled into shell scripts that you can invoke (along with variables, arguments, etc.) to generate complete and well-formatted output text files.
Bash-TPL is presented as a single-file Bash script, making it both easy to bring along with your projects, and even easier to use, since there is no need to compile any source code.
See Adding bash-tpl
into your project for more information.
Creating readable, maintainable templates often requires adding extra whitespace (i.e. indenting lines within a for
loop) in order to help distinguish your template tags from your textual content.
Bash-TPL detects ands removes these extra indentations, resulting in generated text files that look as good as if they were written by hand !
NOTE: Consistent Formatting
The key to success with Bash-TPL's indentation fix-up logic is Consistent Formatting - using consistent indentation throughout your templates will yield best results.
The output of Bash-TPL is a reusable shell script that is then used to generate your final text document(s).
If desired, you could just bring/ship the intermediate script, with no further need for Bash-TPL until you need to make changes to your original template.
The shell scripts that Bash-TPL generates are not intended to be Bash-specific.
Bash-TPL attempts to generate posix printf-compatible statements, utilizing %s
and %b
.
Any shell who's printf matches output below should be compatible:
$ VARIABLE=Variable
$ printf "%b%s%b%s%b\n" 'Text:\t\0047' "$VARIABLE" '\0047 "' "$(echo $VARIABLE)" '" $(\\n)'
Text: 'Variable' "Variable" $(\n)
Although template tags are formatted with %s
by default, you can customize their format using standard printf format specifiers.
See Formatting Text Tags for more information.
Templates can include other templates, making it easy to organize your templates as smaller, reusable components.
NOTE: Bash-TPL's smart indentation tracking & correction even works across included templates !
The indentation of the include
statement dictates the base indentation of the included file.
See INCLUDE directive for more information.
Bash-TPL's default delimiters should accommodate most text file formats without conflict.
But if you do run into conflicts, or if you just prefer a different style, the delimiters are fully configurable.
You can even modify delimiters mid-template !
See Customizing Delimiters for more information.
NOTE: Including Templates With Differing Delimiters
You can easily include templates that have differing delimiters.
If the included template doesn't declare its delimiters explicitly (i.e. maybe it relies on the defaults), you can specify the delimiters as part of the include statement.
See INCLUDE directive for more information.
Bash-TPL has an extensive test suite built wth BATS
Older Bash Versions
The test suite has been tested against Bash versions 3.2
, 4.4
, 5.0
, and 5.1
- Template Tags
- Directives
- Customizing Delimiters
- Resetting Delimiters
- Installing
- Contributing
- Contact
- License
bash-tpl takes a single template file as input and generates a shell script as output.
basic usage
$ bash-tpl my_template.tpl > my_script.sh
In addition to files, bash-tpl can also read directly from stdin
.
To read from stdin
, use a single -
as the input filename:
stdin example
$ bash-tpl - > hello.sh
Hello, world
^D
You can also use other common stdin
sources:
pipe example
$ echo "Hello, world" | bash-tpl - > hello.sh
herestring (<<<
) example
$ bash-tpl - <<< "Hello, world" > hello.sh
heredoc (<<
) example
$ bash-tpl - > hello.sh <<EOF
Hello, world
EOF
NOTE: Use of -
is optional for pipes and herestrings/docs. bash-tpl will try to auto-detect when they are being used. But if you have any issues, an explicit -
should help.
You can specify the output file using the -o
or --output-file
options:
$ bash-tpl --output-file my_script.sh my_template.tpl
display bash-tpl version
$ bash-tpl --version
You can view the full usage screen using the -h
or --help
options, or simply by invoking bash-tpl with no options:
$ bash-tpl --help
Text tags allow you to seamlessly inject dynamic data elements inline with your text.
They allow you to maintain your template as nicely-formatted text instead of complicated print statements, ie:
Here is a simple template example using the standard text tag:
hello.tpl
Hello <% $NAME %>
We can process the template and review the generated script:
process the template
$ bash-tpl hello.tpl
printf "%b%s\n" 'Hello ' "$NAME"
Here's a quick example invoking the template-generated script along with some user data:
usage example
$ NAME=TekWizely source <( bash-tpl hello.tpl )
Hello TekWizely
The default delimiters for standard tags are <%
& %>
See the Customizing Delimiters section to learn about the many ways you can customize delimiters to your liking.
NOTE: For standard tags, The value within the delimiters is 'trimmed' before being processed, ie: '<% $NAME %>'
is equivalent to '<%$NAME%>'
.
This is to encourage you to use liberal amounts of whitespace, keeping your templates easy to read and maintain.
Quote tags are similar to standard tags, with the exception that no trimming is performed, and leading/trailing whitespace is preserved, ie:
Hello <%" $NAME "%>
Is equivalent to :
printf "%b%s\n" 'Hello ' " $NAME " # Whitespace around $NAME is preserved
The default delimiters for Quote Tags are <%"
& "%>
More specifically, the delimiters are : Currently-configured Standard Tag delimiters with leading & trailing quotes ("
)
NOTE: No whitespace is allowed between the standard tag and the quote character (i.e. <% "
would not be treated as a quote tag).
hello.tpl
Hello '<%" <% $NAME %> "%>'
$ NAME=TekWizely source <( bash-tpl hello.tpl )
Hello ' <% TekWizely %> '
Statement tags allow you to inline script statements, printing the output of the statement:
Hello <%% echo $NAME %>
Is equivalent to :
printf "%b%s\n" 'Hello ' "$(echo $NAME)" # Trivial example to demonstrate the conversion
A slightly more useful example of a statement tag might be:
hello.tpl
Hello <%% echo $NAME | tr '[:lower:]' '[:upper:]' %>
$ NAME=TekWizely source <( bash-tpl hello.tpl )
Hello TEKWIZELY
The default delimiters for Statement Tags are <%%
& %>
More specifically, the delimiters are : Currently-configured Standard Tag delimiters with the open delimiter followed by the Statement Tag delimiter.
NOTES:
- The statement Tag delimiter (ie the 2nd
%
) can be configured separate from the Standard Tag delimiters. See Customizing Delimiters for details. - No whitespace is allowed between the Standard Tag and the Statement Tag delimiter (i.e.
<% %
would not be treated as a Statement Tag). - The Statement Tag delimiter is not part of the close tag (
%>
), just the open tag (<%%
)
NOTE: As with Standard Tags, the value within the statement Tag is 'trimmed' before being processed, ie: '<%% echo $NAME %>'
is equivalent to '<%%echo $NAME%>'
.
By default, all Text Tags are rendered with the printf %s
format specifier.
You can use a custom format specifier in your tags:
integer_format.tpl
% myint=123
<%|%d|${myint}%>
process the template
$ bash-tpl integer_format.tpl
myint=123
printf "%d\n" "${myint}"
You can add extra whitespace to make your tags easier to read:
integer_format_spaced.tpl
% myint=123
<% | %d | ${myint} %>
Processing the template generates the same output as the above (no-whitespace) example:
process the template
$ bash-tpl integer_format_spaced.tpl
myint=123
printf "%d\n" "${myint}"
Other than trimming whitespace from the beginning/end of the format specifier, the value is passed as-is to the final output.
This allows you to do things such as use a variable as the format specifier:
integer_format_var.tpl
% myint=123
% myfmt='%05d'
<% | ${myfmt} | ${myint} %>
process the template
$ bash-tpl integer_format_var.tpl
myint=123
myfmt='%05d'
printf "${myfmt}\n" "${myint}"
Invoking the template shows that the output is formatted according to the value of ${myfmt}
:
invoke template script
$ source <( bash-tpl integer_format_var.tpl )
00123
The default delimiters for the format specifier are |
& |
You can customize the delimiters:
customized_format_delimiters.tpl
.DELIMS tag-fmt="[ ]"
% myint=123
<%[%d] $myint %>
process the template
$ bash-tpl customized_format_delimiters.tpl
myint=123
printf "%d\n" "$myint"
See the Customizing Delimiters section to learn about the many ways you can customize delimiters to your liking.
Statement lines allow you to easily insert a single line of script into your template:
test.tpl
% [ -z "$1" ] && echo "Error: Missing argument" >&2 && return
Hello <% $1 %>
test with no arguments
$ source <( bash-tpl test.tpl )
Error: Missing argument
test with argument
$ source <( bash-tpl test.tpl ) TekWizely
Hello TekWizely
Statement blocks allow you to add a full script block into your template:
%
if [ -z "${1}" ]; then
echo "Error: Missing argument" >&2
return
fi
%
Hello <% $1 %>
NOTE: Statement Block delimiters must be on their own line with no other non-whitespace characters.
test with no arguments
$ source <( bash-tpl test.tpl )
Error: Missing argument
test with argument
$ source <( bash-tpl test.tpl ) TekWizely
Hello TekWizely
The default start & stop delimiter for Statement Blocks is %
More specifically, the default is : Currently-configured Statement Line delimiter.
Sometimes parts (or all) of your template may consist of complex code that just needs to print a small amount of text.
You could close and re-open your statement blocks, but that might feel verbose depending on the situation:
verbose.tpl
%
# ...
if (condition); then
%
condition text ...
%
else
%
else text ...
%
fi
# ...
%
Bash-tpl allows you to print single lines of text within your statement blocks:
inline.tpl
%
# ...
if (condition); then
% condition text ...
else
% else text ...
fi
# ...
%
NOTES:
- The indentation of the printed text lines will always match that of the statement block start tag
- This is to encourage you to format your text lines within the context of your script
- All whitespace after the text line tag (
'%
') will be printed as-as
The two techniques generate nearly-identical scripts, but the inline script is slightly easier to read:
verbose.sh
# ...
if (condition); then
printf "%b\n" 'condition text ...'
else
printf "%b\n" 'else text ...'
fi
# ...
inline.sh
# ...
if (condition); then
printf "%b\n" 'condition text ...'
else
printf "%b\n" 'else text ...'
fi
# ...
The default delimiter for a Statement Block Text Line is '% '
(note the trailing space ' '
)
More specifically, the default is : Currently-configured Statement Line delimiter, followed by a space (' '
)
test.tpl
%# This comment will be removed from the template script
% # This comment will remain as part of the template script
Hello world
view raw template script
$ bash-tpl test.tpl
# This comment will remain as part of the template script
printf "%b\n" 'Hello world'
invoke template script
$ source <( bash-tpl test.tpl )
Hello world
test.tpl
ARGS:
%# Due to smart indentation tracking,
%# indentations within the while statement are removed.
% while (( "$#" )); do
%# Indentation is even tracked across includes
.INCLUDE include.tpl
% shift
% done
include.tpl
- <% $1 %>
$ source <( bash-tpl test.tpl ) a b c d
ARGS:
- a
- b
- c
- d
Support for potentially-missing files
You can safely attempt to include a potentially-missing file using .INCLUDE?
no errors if include file is missing
.INCLUDE? optional-file.tpl
NOTES:
- The current delimiter configuration is passed to the included template
- You can pass command-line options (i.e
--tag-delims
,--reset-delims
, etc)
Configure one or more template delimiters from within your template.
test.tpl
%# We change the stmt block and tag delims
%# NOTE: Both the '=' and double-quotes (") are required
.DELIMS stmt-block="<% %>" tag="{{ }}"
<%
if [ -z "${1}" ]; then
echo "Error: Missing argument" >&2
return
fi
%>
Hello {{ $1 }}
$ source <( bash-tpl test.tpl ) TekWizely
Hello TekWizely
NOTES:
- Both the
=
and Double-Quotes ("
) shown in the example script are required - The changes take effect immediately (i.e. the very next line)
- The changes are passed along to any included templates
The DELIMS directive accepts the following parameters:
PARAM | FORMAT | NOTE |
---|---|---|
TAG | ".. .." |
2 two-char sequences, separated by a single space |
TAG-STMT | "." |
1 single character |
TAG-FMT | ". ." |
2 single characters, separated by a single space |
STMT | ".+" |
1 or more characters |
STMT-BLOCK | ".+ .+" |
2 one-or-more char sequences, separated by a single space |
TXT | TEXT | ".+[ ]?" |
1 or more characters, with an optional trailing space |
DIR | DIRECTIVE | ".+" |
1 or more characters |
CMT | COMMENT | ".+" |
1 or more characters |
Reset all delimiters to their default values from within you template.
test.tpl
% echo "default statement delim"
.DELIMS stmt="$>"
$> echo "modified statement delim"
.RESET-DELIMS
% echo "back to default statement delim"
$ source <( bash-tpl test.tpl )
default statement delim
modified statement delim
back to default statement delim
There are several ways to customize delimiters for your templates.
You can change the default delimiters globally via the following environment variables:
VARIABLE | FORMAT | NOTE |
---|---|---|
BASH_TPL_TAG_DELIMS | ".. .." |
2 two-char sequences, separated by a single space |
BASH_TPL_TAG_STMT_DELIM | "." |
1 single character |
BASH_TPL_TAG_FMT_DELIMS | ". ." |
2 single characters, separated by a single space |
BASH_TPL_STMT_DELIM | ".+" |
1 or more characters |
BASH_TPL_STMT_BLOCK_DELIMS | ".+ .+" |
1 one-or-more char sequences, separated by a single space |
BASH_TPL_TEXT_DELIM | ".+[ ]?" |
1 or more characters, with an optional trailing space |
BASH_TPL_DIR_DELIM | ".+" |
1 or more characters |
BASH_TPL_CMT_DELIM | ".+" |
1 or more characters |
test.tpl
%# customize standard tag delimiter
Hello, {{ $1 }}
$ BASH_TPL_TAG_DELIMS="{{ }}" bash-tpl test.tpl > test.sh
$ . test.sh TekWizely
Hello, TekWizely
The following command line options are available for customizing delimiters:
OPTION | FORMAT | NOTE |
---|---|---|
--tag-delims | ".. .." |
2 two-char sequences, separated by a single space |
--tag-stmt-delim | "." |
1 single character |
--tag-fmt-delims | ". ." |
2 single characters, separated by a single space |
--stmt-delim | ".+" |
1 or more characters |
--stmt-block-delims | ".+ .+" |
2 one-or-more char sequences, separated by a single space |
--txt-delim | --text-delim | ".+[ ]?" |
1 or more characters, with an optional trailing space |
--dir-delim | --directive-delim | ".+" |
1 or more characters |
--cmt-delim | --comment-delim | ".+" |
1 or more characters |
NOTE: Command-line options override environment variables.
test.tpl
%# customize standard tag delimiter
Hello, {{ $1 }}
$ bash-tpl --tag-delims "{{ }}" test.tpl > test.sh
$ . test.sh TekWizely
Hello, TekWizely
You can use the DELIMS Directive to configure delimiters from within your template.
NOTE: DELIMS options override both environment variables and command-line options.
There are a couple of ways to reset the delimiters back to their default values.
You can use the --reset-delims
option to reset the delimiters to defaults:
$ bash-tpl --reset-delims test.tpl
NOTES:
- Overrides environment variables
- Overrides any commend-line options that appear before the
--reset-delims
option - Does not override any command-line options that appear after the
--reset-delims
option - Can be passed as an option in an INCLUDE directive, ie:
.INCLUDE my_include.tpl --reset-delims
You can use the RESET-DELIMS Directive to reset delimiters from within your template.
See the Releases page for downloadable archives of versioned releases.
TBD
In addition to working on brew core support, I have also created a tap to ensure the latest version is always available:
install bash-tpl directly from tap
$ brew install tekwizely/tap/bash-tpl
install tap to track updates
$ brew tap tekwizely/tap
$ brew install bash-tpl
Here's a list of some similar tools I discovered before deciding to create bash-tpl :
- mo - mustache templates in bash
- esh - embedded shell (written in sh)
- envsubst (part of gnu gettext)
- shtpl (written in bash)
- shtpl (written in vala)
- cookie (written in bash)
- bash-templater
- renderest (written in bash)
- rc template language
- pp shell-based pre-processor
To contribute to Bash-TPL, follow these steps:
- Fork this repository.
- Create a branch:
git checkout -b <branch_name>
. - Make your changes and commit them:
git commit -m '<commit_message>'
- Push to the original branch:
git push origin <project_name>/<location>
- Create the pull request.
Alternatively see the GitHub documentation on creating a pull request.
If you want to contact me you can reach me at [email protected].
The tekwizely/bash-tpl
project is released under the MIT License. See LICENSE
file.
The bash-tpl
script has an embedded MIT SPDX header, as well as a license header, and should be safe for you to extract and add to your own projects for easy template generation.