Skip to content

Instantly share code, notes, and snippets.

@gmolveau
Last active November 15, 2024 06:59
Show Gist options
  • Save gmolveau/2770f2d05fa5825e1ffdb5a61f0c1283 to your computer and use it in GitHub Desktop.
Save gmolveau/2770f2d05fa5825e1ffdb5a61f0c1283 to your computer and use it in GitHub Desktop.
envsubst alternative in pure bash - replace prefixed environment variables (eg. ${XX_MYVAR}) in a text file
#!/bin/bash
usage() {
echo "Usage: $0 [OPTIONS] [file_path]"
echo "OPTIONS:"
echo " -p, --prefix PREFIX Specify the prefix for environment variables"
echo " -h, --help Show this help message"
echo "If no file_path is provided, the script reads from stdin."
exit 1
}
# Function to replace environment variables with a prefix in a text file
replace_env_variables() {
local content="$1"
local prefix="$2"
# Get environment variables with the specified prefix (only variable names)
variables=$(env | grep "^${prefix}" | sed -e "s/${prefix}\([^=]*\)=.*/\1/")
# Loop through variables and simulate replacement in the content
while IFS= read -r variable; do
key="${prefix}${variable}"
value=$(printenv "${key}")
if [ -n "${value}" ]; then
content=$(echo "${content}" | sed "s|\${${key}}|${value}|g")
fi
done <<< "${variables}"
echo "${content}"
}
# Initialize flags with default values
file_path=""
prefix=""
# Parse flags
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-p | --prefix)
prefix="$2"
shift # past argument
shift # past value
;;
-h | --help)
usage
;;
*)
file_path="$1"
shift # past value
;;
esac
done
# If no file path provided or stdin specified, read from stdin
if [ -z "${file_path}" ] || [ "${file_path}" == "-" ]; then
content=$(cat -)
else
# Check if file exists
if [ ! -f "${file_path}" ]; then
echo "File ${file_path} not found."
exit 1
fi
content=$(< "${file_path}")
fi
replace_env_variables "${content}" "${prefix}"
@rasa
Copy link

rasa commented Feb 5, 2024

The printenv command doesn't exist on OpenWRT. And env dumps function definitions as well as variables. Better to use declare.

Here's a version where the main loop is in pure bash:

envsubst() (
  shopt -s extglob
  local __envsubst_vars __envsubst_line __envsubst_var
  mapfile -t __envsubst_vars < <(declare | grep -E -o '^[^]([:space:]]+=' | cut -d = -f 1)
  while IFS= read -s -r __envsubst_line; do
    for __envsubst_var in "${__envsubst_vars[@]}"; do
      __envsubst_line="${__envsubst_line//\$${__envsubst_var}/${!__envsubst_var}}"
      __envsubst_line="${__envsubst_line//\$\{${__envsubst_var}\}/${!__envsubst_var}}"
    done
    __envsubst_line="${__envsubst_line//\$+([[:word:]])/}"
    __envsubst_line="${__envsubst_line//\$\{+([[:word:]])\}/}"
    printf '%s\n' "${__envsubst_line}"
  done
)

Which you can test with:

export content=""
mapfile -t vars < <(declare | grep -E -o '^[^]([:space:]]+=' | cut -d = -f 1 | sort)
for var in "${vars[@]}"; do
  content+="${var}=\$${var}"$'\n'
done
# shellcheck disable=SC2016
content+='notvars=$# $? $$'$'\n'
# shellcheck disable=SC2016
content+='undefined=$undefined'$'\n'
# shellcheck disable=SC2016
content+='undefined=${undefined}'$'\n'

envsubst <<<"${content}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment