Skip to content

Instantly share code, notes, and snippets.

@bermi
Last active February 4, 2022 17:18
Show Gist options
  • Save bermi/a3f2327e0abd3c722a7194287fe5204d to your computer and use it in GitHub Desktop.
Save bermi/a3f2327e0abd3c722a7194287fe5204d to your computer and use it in GitHub Desktop.
Simple bash and sed templating using delimiters.
#!/usr/bin/env bash
# Simple bash and sed templating using delimiters.
#
# For example, given a template /template-a with the contents
#
# # This is template A
# #
# ### EXAMPLE_TEMPLATE_BEGIN
# echo "Hello from template A"
# ### EXAMPLE_TEMPLATE_END
# #
# # Anything before or after the delimiters is ignored
#
# And a file /file-name with the contents
#
# echo "Some text already on /file-name"
#
# The following command
#
# template append EXAMPLE /template-a </file-name
#
# will generate the following output
#
# echo "Some text already on /file-name"
# ### EXAMPLE_TEMPLATE_BEGIN
# echo "Hello from template A"
# ### EXAMPLE_TEMPLATE_END
#
# calling the command `template append EXAMPLE /template-a </file-name` again will
# not append the template again.
#
# To remove the template call:
#
# template strip EXAMPLE </file-name
#
# To prepend the template call:
#
# template prepend EXAMPLE /template-a </file-name
#
# To replace the template call:
#
# template replace EXAMPLE /template-a </file-name
#
# To inject a template at a '### TEMPLATE_EXAMPLE_PLACEHOLDER' call:
#
# template inject EXAMPLE /template-a </file-name
#
# Note: anything outside _BEGIN / _END tags will be ignored.
# Limitations:
# - Only one template with the same name per file.
# - No support for evaluating variables. Use envsubst for that purpose.
#
# Author: Bermi Ferrer
# License: MIT
# Code and bugs: https://gist.github.com/bermi/a3f2327e0abd3c722a7194287fe5204d
#
set -eEuo pipefail
shopt -s inherit_errexit
IFS=$'\n\t'
get_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
template_name="$(echo -n "$template_name" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
local template
template="$(cat)"
if [ "$(echo "$template" | grep -c "### ${template_name}_TEMPLATE_BEGIN")" != "1" ]; then
echo "Error: template must contain exactly one '### ${template_name}_TEMPLATE_BEGIN' delimiter" >&2
exit 1
fi
if [ "$(echo "$template" | grep -c "### ${template_name}_TEMPLATE_END")" != "1" ]; then
echo "Error: template must contain exactly one '### ${template_name}_TEMPLATE_END' delimiter" >&2
exit 1
fi
echo "$template" | sed -n "/^[ ]*### ${template_name}_TEMPLATE_BEGIN/,\${p;/^[ ]*### ${template_name}_TEMPLATE_END/q}"
}
strip_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
template_name="$(echo -n "$template_name" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
local template
template="$(cat)"
echo "$template" | sed "/^[ ]*### ${template_name}_TEMPLATE_BEGIN/,/^[ ]*### ${template_name}_TEMPLATE_END/d"
}
append_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
local template_path="${2:?You must provide a path to the template file}"
strip_template "$1" | cat - <(echo "") <(get_template "$template_name" <"$template_path")
}
prepend_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
local template_path="${2:?You must provide a path to the template file}"
get_template "$template_name" <"$template_path";
strip_template "$1";
}
replace_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
template_name="$(echo -n "$template_name" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
local template_path="${2:?You must provide a path to the template file}"
# get_template "$template_name" <"$template_path";
# strip_template "$1";
sed -E "s/([ ]*### ${template_name}_TEMPLATE_BEGIN)/### TEMPLATE_${template_name}_PLACEHOLDER\n\1/" | \
sed "/^[ ]*### ${template_name}_TEMPLATE_BEGIN/,/^[ ]*### ${template_name}_TEMPLATE_END/d"
}
inject_template() {
set -eEuo pipefail;
local template_name="${1:?You must provide a template name}"
template_name="$(echo -n "$template_name" | tr '[:lower:]' '[:upper:]' | tr '-' '_')"
local template_path="${2:?You must provide a path to the template file}"
local placeholder="### TEMPLATE_${template_name}_PLACEHOLDER"
local rendered_template_path
rendered_template_path=$(mktemp)
get_template "$template_name" <"$template_path" >"$rendered_template_path"
sed "/${placeholder}/e cat '$rendered_template_path'" | sed "/${placeholder}/d"
rm -rf "$rendered_template_path"
}
if [ "${1:-false}" != "false" ]; then
if [ "$1" = "append" ]; then
shift
append_template "${1:?You must provide a template name}" "${2:?You must provide a path to the template file}"
elif [ "$1" = "prepend" ]; then
shift
prepend_template "${1:?You must provide a template name}" "${2:?You must provide a path to the template file}"
elif [ "$1" = "inject" ]; then
shift
inject_template "${1:?You must provide a template name}" "${2:?You must provide a path to the template file}"
elif [ "$1" = "replace" ]; then
shift
replace_template "${1:?You must provide a template name}" "${2:?You must provide a path to the template file}"
elif [ "$1" = "strip" ]; then
shift
strip_template "${1:?You must provide a template name}"
elif [ "$1" = "get" ]; then
shift
get_template "${1:?You must provide a template name}"
else
echo "Error: Invalid method $1. Valid method are: append, strip and get" >&2
exit 1
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment