Bash global variables

The most reliable and recommended way to share global variables across multiple Bash scripts is to

store them in a dedicated environment file and load them using the source (or .) command.

Because child processes in Linux cannot alter the environment of a parent process, standard execution will isolate variables. Sourcing executes the file within the current shell context, instantly importing all properties.

Step 1: Create the Environment File

Store your variables in a separate configuration or library file (e.g., config.env or globals.sh).

  • Use lowercase or mixed-case for custom globals to avoid overwriting internal system variables like PATH or USER.
  • Use the export keyword if child scripts or binaries executed by the main script also need access.
# config.env
# Custom shared variables
app_version="2.4.1"
database_url="localhost:5432"
log_directory="/var/log/myapp"

export app_version database_url log_directory

Step 2: Retrieve Variables in Your Scripts

To import these variables into any script, find the directory of the executing script and source the file safely.

#!/bin/bash

# Safely locate the directory where this script lives
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)

# Load the global variables 
if [ -f "$script_dir/config.env" ]; then
    source "$script_dir/config.env"
else
    echo "Error: config.env file not found!" >&2
    exit 1
fi

# Use the variables
echo "Starting Application Version: $app_version"
echo "Connecting to Database at: $database_url"

Best Practices for Global Variables

  • Use Unique Prefixes: Prepend a unique short prefix (e.g., cfg_version, cfg_db_url) to your shared variables to prevent naming collisions with local script variables.
  • Make Variables Read-Only: If the values are meant to be constants, define them with declare -r (e.g., declare -r app_version="2.4.1") so they cannot be accidentally overwritten.
  • Quote Every Expansion: Always wrap variables in double quotes when referencing them (e.g., "$log_directory") to ensure the shell handles spaces and special characters perfectly.

Update global variables

Here is how to dynamically update variables inside your configuration file and manage different environments (like development and production).

1. How to Dynamically Update and Save Variables

Because Bash cannot directly "save" its memory back to a file, you must use a stream editor like sed to rewrite the values inside the config.env file.

#!/bin/bash
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE}")" &> /dev/null && pwd)
config_file="$script_dir/config.env"

# Function to safely update a variable in the file
update_env_var() {
    local var_name="$1"
    local new_value="$2"
    
    # Check if the variable already exists in the file
    if grep -q "^${var_name}=" "$config_file"; then
        # Replace the existing line
        sed -i "s|^${var_name}=.*|${var_name}=\"${new_value}\"|" "$config_file"
    else
        # Append the new variable if it does not exist
        echo "${var_name}=\"${new_value}\"" >> "$config_file"
    fi
}

# Example usage: Incrementing a version number
source "$config_file"
new_version="2.4.2"

update_env_var "app_version" "$new_version"
echo "Updated app_version to $new_version inside config.env"

2. Managing Multiple Environments (Dev vs. Prod)

The cleanest approach is to create separate files for each environment and use a single master variable (APP_ENV) to load the correct one.

Step A: Create your environment files

Create specific files side-by-side in your project directory:

# dev.env
database_url="localhost:5432"
log_level="DEBUG"
# prod.env
database_url="10.0.1.50:5432"
log_level="ERROR"

Step B: Load them dynamically in your main script

Use an internal system environment variable or a default fallback to swap configuration profiles instantly:

#!/bin/bash
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE}")" &> /dev/null && pwd)

# Default to 'dev' if the APP_ENV environment variable is not set
env="${APP_ENV:-dev}"
env_file="$script_dir/${env}.env"

# Load the selected environment
if [ -f "$env_file" ]; then
    source "$env_file"
    echo "Loaded configuration for environment: $env"
else
    echo "Error: Configuration file for '$env' not found at $env_file" >&2
    exit 1
fi

# Run your logic using the environment-specific data
echo "Connecting to: $database_url"

Step C: Run the script

You can now toggle environments on the fly right from your terminal command line:

  • Run in Dev: APP_ENV=dev ./myscript.sh
  • Run in Prod: APP_ENV=prod ./myscript.sh

Would you like help writing a validation function to ensure vital variables are present before your script runs, or do you want to see how to securely encrypt sensitive production passwords within these files?

Validating Required Variables

Before running core logic, you should validate that required variables are not empty. This script loops through a list of required variable names, checks if they are set, and terminates safely if any are missing.

#!/bin/bash
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE}")" &> /dev/null && pwd)
source "$script_dir/prod.env"

# Function to validate required variables
validate_env_vars() {
    local missing_vars=0
    # Pass the names of the variables as strings to check them
    local required_vars=("database_url" "log_level" "db_password")

    for var_name in "${required_vars[@]}"; do
        # Indirect expansion: checks the value of the variable named by $var_name
        if [ -z "${!var_name}" ]; then
            echo "Error: Required variable '$var_name' is missing or empty." >&2
            missing_vars=$((missing_vars + 1))
        fi
    done

    if [ $missing_vars -gt 0 ]; then
        echo "Environment validation failed with $missing_vars error(s). Exiting." >&2
        exit 1
    fi
}

# Run the validation
validate_env_vars

Encrypting Sensitive Production Passwords

Storing plaintext passwords in .env files is a major security risk. You can use openssl to securely encrypt your secrets, storing only the encrypted file in your repository.

Step A: Encrypt the file (One-time setup)

Run this command manually in your terminal to encrypt your sensitive production file. It will prompt you to create a decryption password (passphrase):

openssl aes-256-cbc -salt -pbkdf2 -in prod.env -out prod.env.enc
  • prod.env: Your original plaintext file (delete or add to .gitignore after encryption).
  • prod.env.enc: The secure, encrypted file you can safely store.

Step B: Decrypt on the fly inside the script

Instead of saving the decrypted file to the hard drive (which leaks secrets), decrypt the file directly into memory and source it via a file descriptor.

#!/bin/bash
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE}")" &> /dev/null && pwd)
encrypted_file="$script_dir/prod.env.enc"

# Prompt the user or system for the decryption key securely
read -s -p "Enter environment decryption passphrase: " secret_key
echo "" # Print a newline after hidden input

if [ -f "$encrypted_file" ]; then
    # Decrypt directly into memory using a bash process substitution
    source <(openssl aes-256-cbc -d -salt -pbkdf2 -pass pass:"$secret_key" -in "$encrypted_file" 2>/dev/null)
    
    if [ $? -ne 0 ]; then
        echo "Error: Decryption failed. Incorrect passphrase." >&2
        exit 1
    fi
else
    echo "Error: Encrypted environment file not found." >&2
    exit 1
fi

# The variables are now available safely in memory without touching the disk
echo "Securely loaded database: $database_url"

To make this completely hands-free for automation or CI/CD pipelines, you can replace the read -s command and pass the secret passphrase through a secure host system environment variable instead.