Last active
January 23, 2026 14:37
-
-
Save vroomfondel/7ef185d8d6d726fdf720ad0f4ef23619 to your computer and use it in GitHub Desktop.
envsubst implementation in pure bash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Pure Bash implementation of envsubst. | |
| # Usage: ./envsubst.sh [SHELL-FORMAT] | |
| # self-ref | |
| # https://gist.githubusercontent.com/vroomfondel/7ef185d8d6d726fdf720ad0f4ef23619/raw/envsubst.sh | |
| # per curl with cache-busting query-arg | |
| # curl -L https://gist.githubusercontent.com/vroomfondel/7ef185d8d6d726fdf720ad0f4ef23619/raw/envsubst.sh?cache_bust=$(date +%s) | |
| # per api (usage throttled) | |
| # curl -s https://api.github.com/gists/7ef185d8d6d726fdf720ad0f4ef23619 | jq -r '.files["envsubst.sh"].content' | |
| # If arguments were passed, extract the variable names to be replaced. | |
| REPLACE_ONLY="" | |
| if [ $# -gt 0 ]; then | |
| # Extracts variables like $VAR or ${VAR} from the argument string. | |
| REPLACE_ONLY=$(echo "$1" | grep -oE '\$([a-zA-Z_][a-zA-Z0-9_]*|\{[a-zA-Z_][a-zA-Z0-9_]*\})') | |
| fi | |
| # Function to process a single line. | |
| process_line() { | |
| local line="$1" | |
| local result="" | |
| # Regex for $VAR or ${VAR}. | |
| # Matches $ followed by a valid shell variable name or {name}. | |
| local regex='\$([a-zA-Z_][a-zA-Z0-9_]*|\{[a-zA-Z_][a-zA-Z0-9_]*\})' | |
| local remaining="$line" | |
| while [[ "$remaining" =~ $regex ]]; do | |
| local full_match="${BASH_REMATCH[0]}" | |
| local var_name_raw="${BASH_REMATCH[1]}" | |
| # Split the remaining string: before, match, after. | |
| local before="${remaining%%"$full_match"*}" | |
| local after="${remaining#*"$full_match"}" | |
| # Clean the variable name (remove curly braces). | |
| local clean_var_name="${var_name_raw#\{}" | |
| clean_var_name="${clean_var_name%\}}" | |
| # Check if this variable should be replaced. | |
| local should_replace=true | |
| # Only replace if the variable is set in the environment. | |
| if ! [[ -v "$clean_var_name" ]]; then | |
| should_replace=false | |
| fi | |
| # If REPLACE_ONLY is set, only replace if the variable is in the list. | |
| if [ "$should_replace" = true ] && [ -n "$REPLACE_ONLY" ]; then | |
| if ! echo "$REPLACE_ONLY" | grep -qxFe "$full_match"; then | |
| should_replace=false | |
| fi | |
| fi | |
| if [ "$should_replace" = true ]; then | |
| # Read value from the environment. | |
| local var_value="${!clean_var_name}" | |
| result="${result}${before}${var_value}" | |
| else | |
| result="${result}${before}${full_match}" | |
| fi | |
| remaining="$after" | |
| done | |
| result="${result}${remaining}" | |
| printf "%s\n" "$result" | |
| } | |
| # Read from stdin line by line. | |
| # IFS= preserves leading/trailing whitespace. | |
| # -r prevents backslashes from being interpreted. | |
| while IFS= read -r input_line || [ -n "$input_line" ]; do | |
| process_line "$input_line" | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment