Text processing
envsubst
Replace $VAR and ${VAR} references in text with the values of environment variables.
envsubst [OPTION...] [SHELL-FORMAT]
envsubst reads text on standard input, replaces every $NAME and ${NAME} with the value of
that environment variable, and writes the result to standard output. It is the lightweight
way to fill in a configuration template from variables, common in container entrypoints and
deploy scripts, without the quoting hazards of eval or a here-document.
It does exactly one thing. It does not understand defaults like ${PORT:-8080}, command
substitution or arithmetic; it only sees variables that are exported, and by default it
replaces every variable reference, including ones you meant to keep, such as nginx's $uri.
Pass a list of variable names (the SHELL-FORMAT argument) to restrict it.
It comes from GNU gettext (Debian package gettext-base), so it is not installed on every
minimal system.
Examples
Substitute variables in a line of text
echo 'Hello $USER, your home is ${HOME}.' | envsubst
Hello dev, your home is /home/dev.
The single quotes keep the shell from expanding the variables, so envsubst receives them
literally and fills them in from the environment. Both $NAME and ${NAME} forms work.
Render a config file from a template
server {
listen ${PORT};
server_name ${DOMAIN};
root /srv/${APP}/public;
}
export PORT=8080 DOMAIN=demo.example APP=demo
envsubst < app.conf.tpl > app.conf
cat app.conf
server {
listen 8080;
server_name demo.example;
root /srv/demo/public;
} The template's three variables are replaced with the exported values and the result is written to a new file. Keep templates and rendered files separate so you can re-render.
Unexported variables are not seen
PORT=9090
echo 'port=$PORT' | envsubst
PORT=9090 envsubst <<< 'port=$PORT'
port= port=9090
A plain shell variable is not in the environment of child processes, so envsubst replaces
$PORT with an empty string. Setting it in front of the command (or with export)
passes it in.
Unknown variables become empty
echo 'user=$DB_USER pass=$DB_PASSWORD' | DB_USER=app envsubst
user=app pass=
DB_PASSWORD is not set, and envsubst silently substitutes nothing. There is no error and
no way to set a default; check required variables in your script before rendering.
Substitute only selected variables
location / {
proxy_pass http://127.0.0.1:${PORT};
try_files $uri $uri/ =404;
}
export PORT=8080
envsubst '$PORT' < site.tpl
location / {
proxy_pass http://127.0.0.1:8080;
try_files $uri $uri/ =404;
} The argument lists the variables to replace (written with $, in single quotes). nginx's
own $uri is left alone. Without the list, $uri would be replaced by an empty string and
the config would break.
List the variables a template uses
server {
listen ${PORT};
server_name ${DOMAIN};
root /srv/${APP}/public;
}
envsubst --variables "$(cat app.conf.tpl)"
PORT DOMAIN APP
--variables (-v) prints the names referenced in a SHELL-FORMAT string, one per line,
without substituting anything. Passing the whole template as the format is a quick way to
see what a template needs.
Fill a template from a .env file
set -a; . ./.env; set +a
echo 'api=${API_URL} debug=${DEBUG}' | envsubst
api=http://localhost:8080 debug=false
set -a exports every variable assigned while it is on, so sourcing .env puts its
values into the environment. This only works for .env files that are valid shell syntax.
Shell default syntax is not supported
echo 'port=${PORT:-8080}' | envsubst
port=${PORT:-8080} envsubst only recognizes plain $NAME and ${NAME}. The :- form is not a reference to
it, so the text is passed through unchanged. Set defaults in the shell before calling it:
export PORT=${PORT:-8080}.
Common pitfalls
Double-quoting the input (
echo "$HOME" | envsubst) lets the shell substitute first, so envsubst has nothing to do. Use single quotes or a file.Every
$wordin the input is a candidate, including$1-like text in scripts or$uriin nginx configs. Pass an explicit variable list.Missing variables become empty strings with no warning, which can produce a config that parses but is wrong. Validate required variables first, for example with
: "${PORT:?PORT is not set}".Do not render into the template file itself (
envsubst < f > f): the shell truncates it before envsubst reads it.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
envsubst is part of GNU gettext and behaves the same everywhere it is installed. macOS does not include it by default; it comes with the gettext package from Homebrew or MacPorts.
Options worth knowing
A selection, described in our own words. For the full list, see envsubst(1) or envsubst --help.
| Option | What it does |
|---|---|
-v, --variables | Print the variable names in SHELL-FORMAT instead of substituting. |
--help | Show usage and exit. |
-V, --version | Show version information and exit. |
Related
sedEdit a stream of text with a script of commands, most often to substitute text or delete lines.envPrint the environment, or run a command with a modified environment.printenvPrint all environment variables, or the values of the ones you name.exportMark shell variables (or bash functions) to be passed to every program the shell starts.- Environment variablesHow export passes variables to child processes, the one-command NAME=value prefix, loading .env files, and why a child cannot change its parent.
- Here-documents and here-stringsFeed multi-line text to a command with <<EOF, stop expansion with a quoted delimiter, indent with <<-, and pass one string with <<<.
- Parameter expansionThe ${...} operators for defaults, lengths, substrings, trimming prefixes and suffixes, search and replace, and case conversion, without calling sed or cut.