diff --git a/README.md b/README.md index a62b3ec..ec6026f 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,37 @@ Supports default values by writting `{{VAR=value}}` in the template. Sébastien Lavoie -See http://blog.lavoie.sl/2012/11/simple-templating-system-using-bash.html for other details +Johan Haleby + +See http://code.haleby.se/2015/11/20/simple-templating-engine-in-bash/ and http://blog.lavoie.sl/2012/11/simple-templating-system-using-bash.html for more details + +## Installation + +To install templater in linux type: + + sudo curl -L https://raw.githubusercontent.com/johanhaleby/bash-templater/master/templater.sh -o /usr/local/bin/templater + sudo chmod +x /usr/local/bin/templater ## Usage + +```bash +VAR=value templater template +``` + +Read variables from file: + +```bash +templater template -f variables.txt +``` + +e.g.: +```bash +# variables.txt +# The author +AUTHOR=Johan +# The version +VERSION=1.2.3 +``` ```sh # Passing arguments directly @@ -23,6 +51,12 @@ VAR=value templater.sh template # Parentheses are important for not polluting the current shell (set -a && . /tmp/foo && templater.sh template) +# Passing variables to template via a file +templater.sh template -f variables.txt + +# Squelching all warning messages +templater.sh template -s + # A variant that does NOT pass current env variables to the templater sh -c "set -a && . /tmp/foo && templater.sh template" ``` diff --git a/templater.sh b/templater.sh index b6fe194..0053eb8 100755 --- a/templater.sh +++ b/templater.sh @@ -1,19 +1,133 @@ #!/bin/bash +# +# Very simple templating system that replaces {{VAR}} by the value of $VAR. +# Supports default values by writting {{VAR=value}} in the template. +# +# Copyright (c) 2017 Sébastien Lavoie +# Copyright (c) 2017 Johan Haleby +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# See: https://github.com/johanhaleby/bash-templater +# Version: https://github.com/johanhaleby/bash-templater/commit/5ac655d554238ac70b08ee4361d699ea9954c941 + # Replaces all {{VAR}} by the $VAR value in a template file and outputs it -# Use with -h to output all variables -if [[ ! -f "$1" ]]; then - echo "Usage: VAR=value $0 template" >&2 +readonly PROGNAME=$(basename $0) + +config_file="" +print_only="false" +silent="false" + +usage="${PROGNAME} [-h] [-d] [-f] [-s] [-o] -- + +where: + -h, --help + Show this help text + -p, --print + Don't do anything, just print the result of the variable expansion(s) + -f, --file + Specify a file to read variables from + -s, --silent + Don't print warning messages (for example if no variables are found) + -o, --vars-optional + Allow for no variables to be present (simply returns the template as is) + +examples: + VAR1=Something VAR2=1.2.3 ${PROGNAME} test.txt + ${PROGNAME} test.txt -f my-variables.txt + ${PROGNAME} test.txt -f my-variables.txt > new-test.txt" + +if [ $# -eq 0 ]; then + echo "$usage" + exit 1 +fi + +if [[ ! -f "${1}" ]]; then + echo "You need to specify a template file" >&2 + echo "$usage" exit 1 fi -template="$1" -vars=$(grep -oE '\{\{\s*[A-Za-z0-9_]+\s*\}\}' "$template" | sort | uniq | sed -e 's/^{{//' -e 's/}}$//') +template="${1}" + +if [ "$#" -ne 0 ]; then + while [ "$#" -gt 0 ] + do + case "$1" in + -h|--help) + echo "$usage" + exit 0 + ;; + -p|--print) + print_only="true" + ;; + -f|--file) + config_file="$2" + ;; + -s|--silent) + silent="true" + ;; + -o|--vars-optional) + vars_optional="true" + ;; + --) + break + ;; + -*) + echo "Invalid option '$1'. Use --help to see the valid options" >&2 + exit 1 + ;; + # an option argument, continue + *) ;; + esac + shift + done +fi + +vars=$(grep -oE '\{\{[A-Za-z0-9_]+\}\}' "${template}" | sort | uniq | sed -e 's/^{{//' -e 's/}}$//') if [[ -z "$vars" ]]; then - echo "Warning: No variable was found in $template, syntax is {{VAR}}" >&2 + if [ "$vars_optional" == "true" ]; then + if [ "$print_only" == "false" ]; then + cat $template + fi + exit 0 + else + if [ "$silent" == "false" ]; then + echo "Warning: No variable was found in ${template}, syntax is {{VAR}}" >&2 + fi + exit 1 + fi fi +# Load variables from file if needed +if [ "${config_file}" != "" ]; then + if [[ ! -f "${config_file}" ]]; then + echo "The file ${config_file} does not exists" >&2 + echo "$usage" + exit 1 + fi + + source "${config_file}" +fi + var_value() { var="${1}" eval echo \$"${var}" @@ -48,8 +162,7 @@ replaces=() # Reads default values defined as {{VAR=value}} and delete those lines # There are evaluated, so you can do {{PATH=$HOME}} or {{PATH=`pwd`}} # You can even reference variables defined in the template before -defaults=$(grep -oE '^\{\{[A-Za-z0-9_]+=.+\}\}$' "${template}" | sed -e 's/^{{//' -e 's/}}$//') -IFS=$'\n' +defaults=$(grep -oE '^\{\{[A-Za-z0-9_]+=.+\}\}' "${template}" | sed -e 's/^{{//' -e 's/}}$//') for default in $defaults; do var=$(echo "${default}" | grep -oE "^[A-Za-z0-9_]+") current="$(var_value "${var}")" @@ -69,7 +182,7 @@ done vars="$(echo "${vars}" | tr " " "\n" | sort | uniq)" -if [[ "$2" = "-h" ]]; then +if [[ "$print_only" == "true" ]]; then for var in $vars; do value="$(var_value "${var}")" echo_var "${var}" "${value}" @@ -79,15 +192,17 @@ fi # Replace all {{VAR}} by $VAR value for var in $vars; do - value="$(var_value "${var}")" + value=$(var_value $var | sed -e "s;\&;\\\&;g" -e "s;\ ;\\\ ;g") # '&' and is escaped if [[ -z "$value" ]]; then - echo "Warning: $var is not defined and no default is set, replacing by empty" >&2 + if [ $silent == "false" ]; then + echo "Warning: $var is not defined and no default is set, replacing by empty" >&2 + fi fi # Escape slashes - value="$(escape_chars "${value}" "\\" '/' ' ')"; - replaces+=("-e") - replaces+=("s/{{\s*${var}\s*}}/${value}/g") + value=$(echo "$value" | sed 's/\//\\\//g'); + replaces="-e 's/{{$var}}/${value}/g' $replaces" done -sed "${replaces[@]}" "${template}" +escaped_template_path=$(echo $template | sed 's/ /\\ /g') +eval sed $replaces "$escaped_template_path" \ No newline at end of file