Home / Alt manpages / git-sh-i18n--envsubst(1)

  • git-sh-i18n--envsubst(1)
  • User command
  • linux

Use Git's Internal envsubst Safely in Shell Scripts

git sh-i18n--envsubst is Git's tiny internal variable substitution tool, buried in its shell plumbing rather than built for direct use. This guide shows how to use it without mistaking it for a general templating engine. The installed system provides Git 2.43.0 from the git-man package version 1:2.43.0-1ubuntu7.3. Allow about ten minutes. You need Git and a POSIX-compatible shell.

This is a plumbing command used by Git's internationalisation scripts. The local manual explicitly says it is not intended for normal end users, makes no interface-stability promise and may disappear. Treat the examples as useful for studying or extending Git shell scripts, not as a new application dependency.

1. Check the installed Git version

Start with an ordinary, read-only check. It needs no elevated privileges:

$ git --version
git version 2.43.0
$ dpkg-query -W -f='${Package} ${Version}\n' git-man
git-man 1:2.43.0-1ubuntu7.3

Package versions vary by distribution. Keep this result with any bug report or script review, because this helper is an internal interface rather than a compatibility target.

Checkpoint

If git --version fails, install Git through your normal system package process before continuing. Do not run this helper through a copied path from another machine.

2. Prepare a controlled template

The helper takes the template as its first argument and reads the text to transform from standard input, expanding only the variables named in that template. The safest starting point is a fixed template with a deliberately small set of names:

$ export PROJECT_NAME='demo'
$ export PROJECT_ROOT='/srv/demo'
$ printf '%s\n' 'Project: $PROJECT_NAME; root: ${PROJECT_ROOT}' | \
    git sh-i18n--envsubst '$PROJECT_NAME ${PROJECT_ROOT}'
Project: demo; root: /srv/demo

The single quotes around the template matter: they stop the calling shell expanding the variables before Git receives them. The pipe supplies the input text, while the final quoted argument describes which variable references are eligible for replacement.

The command prints transformed text to standard output and returns status 0 for this successful case:

$ printf '%s\n' 'Project: $PROJECT_NAME' | git sh-i18n--envsubst '$PROJECT_NAME' > /tmp/envsubst-result
$ printf 'status: %s\n' "$?"
status: 0
$ cat /tmp/envsubst-result
Project: demo

The temporary file is only a verification aid. Remove it once checked with rm -f /tmp/envsubst-result; that deletion is safe here because the example creates no valuable state. For real output, choose a destination deliberately and avoid overwriting a useful file with an unreviewed redirect.

3. Inspect variable names without substituting

Use --variables when a calling script needs to discover the names present in a template. The template is still the first argument; standard input can carry the text that would otherwise be transformed:

$ printf '%s\n' 'ignored input' | \
    git sh-i18n--envsubst --variables '$PROJECT_NAME ${PROJECT_ROOT} $UNSET_VALUE $1'
PROJECT_NAME
PROJECT_ROOT
UNSET_VALUE

Shell positional parameters such as $1 are not listed. The helper recognises ordinary variable references, including the braced form, and reports one name per line. An unset ordinary variable is still reported by --variables, while normal substitution leaves its reference empty:

$ unset UNSET_VALUE
$ printf '%s\n' 'name=$PROJECT_NAME missing=$UNSET_VALUE' | \
    git sh-i18n--envsubst '$PROJECT_NAME $UNSET_VALUE'
name=demo missing=

That empty result is a common distraction in deployment scripts. Check required variables before interpolation if an empty value would make the generated text unsafe or unusable:

: "${PROJECT_NAME:?set PROJECT_NAME before rendering}"
: "${PROJECT_ROOT:?set PROJECT_ROOT before rendering}"
printf '%s\n' 'Project: $PROJECT_NAME; root: ${PROJECT_ROOT}' | \
    git sh-i18n--envsubst '$PROJECT_NAME ${PROJECT_ROOT}'

The parameter checks are shell syntax, not options Git provides. They stop the script before it generates output when a required value is missing.

4. Keep the helper inside the i18n workflow

Git's documented use is within the eval_gettext function in git-sh-i18n. That function supplies a template, exports the variable names found by --variables, then passes the same template to the substitution helper. A reduced shape looks like this:

eval_gettext () {
    printf '%s' "$1" | (
        export PATH $(git sh-i18n--envsubst --variables "$1")
        git sh-i18n--envsubst "$1"
    )
}

PROJECT_NAME='demo'
export PROJECT_NAME
eval_gettext 'Project: $PROJECT_NAME'

The example mirrors the installed manual's synopsis. It is useful for understanding the plumbing, but do not copy it into a new application merely to get variable interpolation. If you own a general shell script, use a supported tool whose interface and security properties actually match that script's needs, and do not confuse this helper with a general-purpose template engine or with shell evaluation.

5. Avoid accidental shell execution

The helper substitutes variable references in text. It does not make the resulting text safe to pass to eval, a shell command line, SQL, a configuration parser or an HTML document. Keep the result as data, quote it for its eventual consumer and validate it there. Never add eval just because the function name in Git's i18n workflow contains that word.

Warning

Do not place untrusted input into the template argument. A shell expands command substitutions before Git runs, so this is dangerous when the template comes from outside the script:

# Do not do this with untrusted TEMPLATE:
git sh-i18n--envsubst "$TEMPLATE"

Prefer a fixed template and pass untrusted values through environment variables after applying the validation the destination format requires. Also avoid exporting more names than the template needs: the --variables workflow deliberately narrows the exported set, which makes accidental data leakage less likely.

6. Diagnose the usual mistakes

If input is passed on standard input without a template argument, the installed helper rejects the request instead of substituting every variable:

$ printf '%s\n' '$PROJECT_NAME' | git sh-i18n--envsubst
error: we won't substitute all variables on stdin for you

Supply a quoted template argument to make the allowed names explicit. If the output still contains $NAME, check that the template includes that exact variable name, that the variable is exported or otherwise present in the helper's environment, and that the shell did not expand it before Git received it. If output is unexpectedly empty, check for an unset variable and use the required-variable checks from step 3.

There is no service restart, configuration file edit or privilege escalation in this workflow. Do not use sudo to run it. If a script has already written bad generated output, recover by restoring the destination from its normal backup or version-control process, then fix the template and rerun the read-only examples first.

Done means

  • You confirmed the Git version and recorded it when testing behaviour.
  • You passed a fixed, quoted template as the first argument and supplied input on standard input.
  • You used --variables to inspect the names a template references.
  • You checked required values before rendering when empty substitutions would be unsafe.
  • You kept the helper limited to Git i18n plumbing and did not treat it as a stable public interface.