diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3c14744
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,2 @@
+language: bash
+script: ./test.sh
diff --git a/README.md b/README.md
index 89b5e82..70ba73d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
# BASH Templater
-Very simple templating system that replace {{VAR}} by $VAR environment value
-Supports default values by writting {{VAR=value}} in the template
+
+Very simple templating system that replace `{{VAR}}` by `$VAR` environment value.
+
+Supports default values by writting `{{VAR=value}}` in the template.
+
+[](https://travis-ci.org/lavoiesl/bash-templater)
## Author
@@ -12,36 +16,38 @@ See http://code.haleby.se/2015/11/20/simple-templating-engine-in-bash/ and http
## Installation
-To install templater in linux type:
+`templater.sh` has no external dependencies. You can use it by directly executing.
+
+To install `templater.sh` globally 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
+ sudo curl -L https://raw.githubusercontent.com/johanhaleby/bash-templater/master/templater.sh -o /usr/local/bin/templater.sh
+ sudo chmod +x /usr/local/bin/templater.sh
## Usage
-```bash
-VAR=value templater template
+VAR=value templater.sh template
```
Read variables from file:
```bash
-templater template -f variables.txt
+templater.sh template -f variables.txt
```
-e.g.:
```bash
-# variables.txt
-# The author
-AUTHOR=Johan
-# The version
-VERSION=1.2.3
-```
+# Using external configuration file (and don't print the warnings)
+templater.sh template -f variables.txt -s
-Don't print any warning messages:
+# Passing arguments directly
+VAR=value templater.sh template
-```bash
-templater template -f variables.txt -s
+# Evaluate /tmp/foo and pass those variables to the template
+# Useful for defining variables in a file
+# Parentheses are important for not polluting the current shell
+(set -a && . /tmp/foo && templater.sh template)
+
+# A variant that does NOT pass current env variables to the templater
+sh -c "set -a && . /tmp/foo && templater.sh template"
```
## Examples
diff --git a/examples/vhost-php.conf b/examples/vhost-php.conf
index 682dacf..e7767a0 100644
--- a/examples/vhost-php.conf
+++ b/examples/vhost-php.conf
@@ -1,17 +1,11 @@
-{{LOG_DIR=/var/log/apache2}}
-{{RUN_DIR=/var/run/php-fpm}}
-{{FCGI=$RUN_DIR/$DOMAIN.fcgi}}
-{{SOCKET=$RUN_DIR/$DOMAIN.sock}}
-{{EMAIL=$USER@$DOMAIN}}
-{{DOC_ROOT=/home/$USER/sites/$DOMAIN/htdocs}}
- ServerAdmin {{EMAIL}}
- ServerName {{DOMAIN}}
- ServerAlias www.{{DOMAIN}}
+ ServerAdmin nobody@example.com
+ ServerName example.com
+ ServerAlias www.example.com
- DocumentRoot "{{DOC_ROOT}}"
+ DocumentRoot "/home/nobody/sites/example.com/htdocs"
-
+
AllowOverride All
Order allow,deny
Allow From All
@@ -19,10 +13,10 @@
AddHandler php-script .php
Action php-script /php5.fastcgi virtual
- Alias /php5.fastcgi {{FCGI}}
- FastCGIExternalServer {{FCGI}} -socket {{SOCKET}}
+ Alias /php5.fastcgi /var/run/php-fpm/example.com.fcgi
+ FastCGIExternalServer /var/run/php-fpm/example.com.fcgi -socket /var/run/php-fpm/example.com.sock
LogLevel warn
- CustomLog {{LOG_DIR}}/{{DOMAIN}}.access.log combined
- ErrorLog {{LOG_DIR}}/{{DOMAIN}}.error.log
+ CustomLog /var/log/apache2/example.com.access.log combined
+ ErrorLog /var/log/apache2/example.com.error.log
diff --git a/examples/vhost-php.tpl.conf b/examples/vhost-php.tpl.conf
new file mode 100644
index 0000000..2e773f7
--- /dev/null
+++ b/examples/vhost-php.tpl.conf
@@ -0,0 +1,28 @@
+{{LOG_DIR=/var/log/apache2}}
+{{RUN_DIR=/var/run/php-fpm}}
+{{FCGI=$RUN_DIR/$DOMAIN.fcgi}}
+{{SOCKET=$RUN_DIR/$DOMAIN.sock}}
+{{EMAIL=$USER@$DOMAIN}}
+{{DOC_ROOT=/home/$USER/sites/$DOMAIN/htdocs}}
+
+ ServerAdmin {{EMAIL}}
+ ServerName {{DOMAIN}}
+ ServerAlias www.{{ DOMAIN }}
+
+ DocumentRoot "{{DOC_ROOT}}"
+
+
+ AllowOverride All
+ Order allow,deny
+ Allow From All
+
+
+ AddHandler php-script .php
+ Action php-script /php5.fastcgi virtual
+ Alias /php5.fastcgi {{FCGI}}
+ FastCGIExternalServer {{ FCGI }} -socket {{SOCKET}}
+
+ LogLevel warn
+ CustomLog {{LOG_DIR}}/{{DOMAIN}}.access.log combined
+ ErrorLog {{LOG_DIR }}/{{ DOMAIN}}.error.log
+
diff --git a/templater.sh b/templater.sh
index e96f2ff..af11b00 100755
--- a/templater.sh
+++ b/templater.sh
@@ -34,6 +34,8 @@ readonly PROGNAME=$(basename $0)
config_file=""
print_only="false"
silent="false"
+nounset="false"
+verbose="false"
usage="${PROGNAME} [-h] [-d] [-f] [-s] --
@@ -46,6 +48,10 @@ where:
Specify a file to read variables from
-s, --silent
Don't print warning messages (for example if no variables are found)
+ -u, --nounset
+ Unset variables throws error instead of a warning
+ -v, --verbose
+ Verbose output
examples:
VAR1=Something VAR2=1.2.3 ${PROGNAME} test.txt
@@ -57,13 +63,6 @@ if [ $# -eq 0 ]; then
exit 1
fi
-if [[ ! -f "${1}" ]]; then
- echo "You need to specify a template file" >&2
- echo "$usage"
- exit 1
-fi
-
-template="${1}"
if [ "$#" -ne 0 ]; then
while [ "$#" -gt 0 ]
@@ -76,32 +75,47 @@ if [ "$#" -ne 0 ]; then
-p|--print)
print_only="true"
;;
- -f|--file)
- config_file="$2"
+ -f|--file) shift
+ config_file="$1"
;;
-s|--silent)
silent="true"
;;
- --)
- break
+ -u|--nounset)
+ nounset="true"
+ ;;
+ -v|--verbose)
+ verbose="true"
;;
-*)
echo "Invalid option '$1'. Use --help to see the valid options" >&2
exit 1
;;
- # an option argument, continue
- *) ;;
+ # an option argument, this must be the template
+ *)
+ template="$1"
+ ;;
esac
shift
done
fi
-vars=$(grep -oE '\{\{[A-Za-z0-9_]+\}\}' "${template}" | sort | uniq | sed -e 's/^{{//' -e 's/}}$//')
+if [[ ! -f "$template" ]]; then
+ echo "You need to specify a template file" >&2
+ echo "$usage"
+ exit 1
+fi
+
+
+
+vars=$(grep -oE '\{\{\s*[A-Za-z0-9_]+\s*\}\}' "$template" | sort | uniq | sed -e 's/^{{//' -e 's/}}$//')
if [[ -z "$vars" ]]; then
- if [ "$silent" == "false" ]; then
- echo "Warning: No variable was found in ${template}, syntax is {{VAR}}" >&2
+ if [ "$verbose" == "true" ]; then
+ echo "Warning: No variable was found in ${template}" >&2
fi
+ cat $template
+ exit 0
fi
# Load variables from file if needed
@@ -111,42 +125,72 @@ if [ "${config_file}" != "" ]; then
echo "$usage"
exit 1
fi
-
+ _pwd=$PWD
+ cd "$(dirname "$config_file")"
source "${config_file}"
+ cd "$_pwd"
fi
var_value() {
- eval echo \$$1
+ var="${1}"
+ eval echo \$"${var}"
+}
+
+##
+# Escape custom characters in a string
+# Example: escape "ab'\c" '\' "'" ===> ab\'\\c
+#
+function escape_chars() {
+ local content="${1}"
+ shift
+
+ for char in "$@"; do
+ content="${content//${char}/\\${char}}"
+ done
+
+ echo "${content}"
+}
+
+function echo_var() {
+ local var="${1}"
+ local content="${2}"
+ local escaped="$(escape_chars "${content}" "\\" '"')"
+
+ echo "${var}=\"${escaped}\""
}
-replaces=""
+declare -a replaces
+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/}}$//')
-
+#????defaults=$(grep -oE '^\{\{[A-Za-z0-9_]+=.+\}\}$' "${template}" | sed -e 's/^{{//' -e 's/}}$//')
+IFS=$'\n'
for default in $defaults; do
- var=$(echo "$default" | grep -oE "^[A-Za-z0-9_]+")
- current=`var_value $var`
+ var=$(echo "${default}" | grep -oE "^[A-Za-z0-9_]+")
+ current="$(var_value "${var}")"
# Replace only if var is not set
- if [[ -z "$current" ]]; then
- eval $default
+ if [[ -n "$current" ]]; then
+ eval "$(echo_var "${var}" "${current}")"
+ else
+ eval "${default}"
fi
# remove define line
- replaces="-e '/^{{$var=/d' $replaces"
- vars="$vars
-$current"
+ replaces+=("-e")
+ replaces+=("/^{{${var}=/d")
+ vars="${vars} ${var}"
done
-vars=$(echo $vars | sort | uniq)
+vars="$(echo "${vars}" | tr " " "\n" | sort | uniq)"
if [[ "$print_only" == "true" ]]; then
for var in $vars; do
- value=`var_value $var`
- echo "$var = $value"
+ value="$(var_value "${var}")"
+ echo_var "${var}" "${value}"
done
exit 0
fi
@@ -155,15 +199,18 @@ fi
for var in $vars; do
value=$(var_value $var | sed -e "s;\&;\\\&;g" -e "s;\ ;\\\ ;g") # '&' and is escaped
if [[ -z "$value" ]]; then
- if [ $silent == "false" ]; then
- echo "Warning: $var is not defined and no default is set, replacing by empty" >&2
+ if [[ $nounset == "false" ]]; then
+ [ $silent == "true" ] || echo "Warning: $var is not defined and no default is set, replacing by empty" >&2
+ else
+ echo "ERROR: $var is not defined and no default is set." >&2
+ exit 1
fi
fi
# Escape slashes
- value=$(echo "$value" | sed 's/\//\\\//g');
- replaces="-e 's/{{$var}}/${value}/g' $replaces"
+ value="$(escape_chars "${value}" "\\" '/' ' ')";
+ replaces+=("-e")
+ replaces+=("s/{{\s*${var}\s*}}/${value}/g")
done
-escaped_template_path=$(echo $template | sed 's/ /\\ /g')
-eval sed $replaces "$escaped_template_path"
+sed "${replaces[@]}" "${template}"
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..56af89b
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+if diff -u <(USER=nobody DOMAIN=example.com ./templater.sh examples/vhost-php.tpl.conf) examples/vhost-php.conf; then
+ echo OK
+else
+ echo Differences found
+ exit 1
+fi