← Back to DevBytes

Zsh Scripting: Environment Variables Complete Guide

Introduction to Environment Variables in Zsh

Environment variables are one of the most powerful and frequently used features in any Unix-like shell, and Zsh (Z shell) is no exception. They act as dynamic values that affect the behavior of processes running on your system. Whether you are configuring paths, storing API keys, or customizing your prompt, environment variables are the backbone of shell scripting and system configuration.

In this complete guide, we will explore what environment variables are, why they matter, how to create and manipulate them in Zsh, and the best practices you should follow when working with them in your scripts.

What Are Environment Variables?

An environment variable is a named value stored in the environment of a process. When a shell starts, it inherits a set of variables from its parent process. These variables are accessible to the shell and any child processes it spawns. In Zsh, variables can be either shell variables (local to the current shell) or environment variables (exported and available to child processes).

Common examples of environment variables include PATH, HOME, USER, SHELL, and LANG. These variables tell programs where to find executables, who the current user is, and what language settings to use.

Shell Variables vs Environment Variables

Understanding the difference between shell variables and environment variables is crucial:

# Shell variable - not available to child processes
MY_VAR="hello"

# Environment variable - available to child processes
export MY_ENV_VAR="world"

# Verify with env command
env | grep MY_

Why Environment Variables Matter

Environment variables matter because they provide a flexible, standardized way to configure applications without hardcoding values into source code or scripts. They enable:

Viewing Environment Variables in Zsh

Zsh provides several built-in commands to inspect environment variables. Knowing how to view them is the first step in mastering their usage.

Listing All Environment Variables

To see all exported environment variables, use the env or printenv command:

# Print all environment variables
env

# Print a specific variable
printenv PATH

# Print all variables including shell variables
set

Accessing a Single Variable

To access the value of a variable, prefix it with a dollar sign $:

# Print the value of HOME
echo $HOME

# Print the value of USER
echo $USER

# Use in a string
echo "Hello, $USER! Your home is $HOME"

Using Parameter Expansion

Zsh supports powerful parameter expansion features that go beyond simple variable access:

# Default value if variable is unset
echo ${MY_VAR:-default_value}

# Assign default if unset
echo ${MY_VAR:=default_value}

# Length of variable value
echo ${#PATH}

# Uppercase (Zsh specific)
echo ${PATH:u}

# Lowercase (Zsh specific)
echo ${PATH:l}

Creating and Setting Environment Variables

Creating environment variables in Zsh is straightforward. You assign a value to a name and then export it.

Basic Assignment and Export

# Assign and export in one line
export DATABASE_URL="postgres://localhost:5432/mydb"

# Assign first, then export
APP_MODE="production"
export APP_MODE

# Verify
echo $DATABASE_URL
echo $APP_MODE

Appending to Existing Variables

A common task is appending a new directory to the PATH variable. This is essential when installing new tools:

# Append to PATH
export PATH="$PATH:/usr/local/bin"

# Prepend to PATH (higher priority)
export PATH="/usr/local/bin:$PATH"

# Add a custom scripts directory
export PATH="$HOME/.local/bin:$PATH"

Setting Variables Temporarily

You can set an environment variable for a single command without affecting the current shell:

# Run a command with a temporary variable
NODE_ENV=production node app.js

# Multiple temporary variables
DATABASE_URL="postgres://localhost/mydb" APP_PORT=3000 ./start.sh

This technique is extremely useful for one-off commands where you do not want to pollute your shell environment.

Working with Variables in Zsh Scripts

When writing Zsh scripts, environment variables play a critical role in making your scripts flexible and reusable.

Reading User Input into Variables

#!/usr/bin/env zsh

# Prompt user for input
echo "Enter your name:"
read username

# Export as environment variable
export GREETING_USER="$username"

echo "Hello, $GREETING_USER!"

Reading Variables from a File

A common pattern is to store configuration in a file and source it:

# config.env file
export API_KEY="abc123"
export API_BASE_URL="https://api.example.com"
export DEBUG="true"
#!/usr/bin/env zsh

# Source the configuration file
source ./config.env

# Use the variables
echo "API Base URL: $API_BASE_URL"
echo "Debug mode: $DEBUG"

if [[ "$DEBUG" == "true" ]]; then
  echo "Debugging is enabled"
fi

Checking if a Variable Is Set

#!/usr/bin/env zsh

# Check if variable is set and non-empty
if [[ -n "$API_KEY" ]]; then
  echo "API key is configured"
else
  echo "Error: API_KEY is not set"
  exit 1
fi

# Check if variable is set (even if empty)
if [[ -v API_KEY ]]; then
  echo "API_KEY variable exists"
fi

Special Environment Variables in Zsh

Zsh has several special environment variables that control its behavior. Understanding these can help you customize your shell experience.

Common Special Variables

Zsh-Specific Arrays

Zsh treats certain variables as arrays, which is different from Bash:

# PATH is an array in Zsh
echo $path       # array version (lowercase)
echo $PATH       # colon-separated string version

# Add to PATH using array syntax
path+=("/usr/local/bin")

# Print each element on its own line
for dir in $path; do
  echo $dir
done

Persistent Environment Variables

Environment variables set in a terminal session are lost when the session closes. To make them persistent, you need to add them to a Zsh configuration file.

Zsh Configuration Files

Adding Persistent Variables

# Add environment variables to ~/.zshenv
# This ensures they are available in all Zsh invocations

# Open the file
nano ~/.zshenv

# Add your variables
export EDITOR="nvim"
export GOPATH="$HOME/go"
export PATH="$PATH:$GOPATH/bin"
export PROJECTS_DIR="$HOME/projects"

# Save and reload
source ~/.zshenv

Advanced Variable Techniques

Variable Indirection

Zsh supports indirect variable expansion, which allows you to reference a variable whose name is stored in another variable:

#!/usr/bin/env zsh

VAR_NAME="HOME"
# Indirect expansion using ${(P)}
echo ${(P)VAR_NAME}

# Practical example
config_key="DATABASE_URL"
echo "Config value: ${(P)config_key}"

String Manipulation

#!/usr/bin/env zsh

MY_STRING="Hello, World!"

# Substring
echo ${MY_STRING:0:5}      # Hello

# Remove from beginning
FILE="archive.tar.gz"
echo ${FILE#*.}            # tar.gz (shortest match)
echo ${FILE##*.}           # gz (longest match)

# Remove from end
echo ${FILE%.*}            # archive.tar (shortest match)
echo ${FILE%%.*}           # archive (longest match)

# Replace
echo ${MY_STRING/World/Zsh}      # Hello, Zsh!
echo ${MY_STRING//l/L}           # HeLLo, WorLd!

Arrays and Environment Variables

#!/usr/bin/env zsh

# Define an array
fruits=("apple" "banana" "cherry")

# Access elements
echo $fruits[1]      # apple (Zsh uses 1-based indexing)
echo $fruits[2]      # banana

# Number of elements
echo ${#fruits}      # 3

# Iterate
for fruit in $fruits; do
  echo "Fruit: $fruit"
done

# Join array into a string
joined=$(IFS=,; echo "${fruits[*]}")
echo $joined         # apple,banana,cherry

Best Practices for Environment Variables

1. Use .zshenv for Environment Variables

Place environment variables in ~/.zshenv rather than ~/.zshrc. This ensures they are available in non-interactive contexts like scripts and cron jobs.

2. Quote Your Variables

Always quote variables to prevent word splitting and glob expansion issues:

# Bad - may break with spaces in paths
cp $FILE $DESTINATION

# Good - safe with spaces
cp "$FILE" "$DESTINATION"

# Best - use curly braces for clarity
cp "${FILE}" "${DESTINATION}"

3. Use Descriptive Names

Use uppercase names for exported environment variables and lowercase for local script variables. This follows the Unix convention and avoids conflicts with shell built-ins:

# Environment variables (exported)
export MY_APP_DATABASE_HOST="localhost"
export MY_APP_LOG_LEVEL="debug"

# Local script variables
local config_file="./config.env"
local retry_count=3

4. Never Hardcode Secrets

Store sensitive information like API keys, passwords, and tokens in environment variables loaded from a file that is not committed to version control:

# .env file (add to .gitignore)
export SECRET_API_KEY="sk-xxxxxxxxxxxx"
export DB_PASSWORD="supersecret"

# In your script
source ./.env

# Use the secret
curl -H "Authorization: Bearer $SECRET_API_KEY" https://api.example.com/data

5. Validate Required Variables

Always check that required environment variables are set before proceeding with your script logic:

#!/usr/bin/env zsh

required_vars=("API_KEY" "API_SECRET" "DB_URL")

for var in $required_vars; do
  if [[ -z "${(P)var}" ]]; then
    echo "Error: $var is not set"
    exit 1
  fi
done

echo "All required variables are set. Proceeding..."

6. Use Local Variables in Functions

When writing functions, use local to avoid polluting the global namespace:

#!/usr/bin/env zsh

greet_user() {
  local name="$1"
  local greeting="Hello"
  echo "$greeting, $name!"
}

greet_user "Alice"
# $name and $greeting are not accessible outside the function

7. Unset Variables When Done

If you set temporary variables in a script, consider unsetting them when they are no longer needed:

#!/usr/bin/env zsh

export TEMP_TOKEN="abc123"

# Use the token
make_api_call "$TEMP_TOKEN"

# Clean up
unset TEMP_TOKEN

Debugging Environment Variables

When things go wrong, debugging environment variables can save you hours of frustration. Zsh provides tools to help you trace variable usage.

Tracing Variable Expansion

#!/usr/bin/env zsh

# Enable xtrace to see expanded commands
set -x

MY_VAR="test"
echo "Value is $MY_VAR"

# Disable xtrace
set +x

Checking Variable Origin

# Find where a variable is set in your config files
grep -rn "export PATH" ~/.zshenv ~/.zshrc ~/.zprofile 2>/dev/null

# Check if a variable is exported
typeset -x | grep MY_VAR

Conclusion

Environment variables are an essential part of Zsh scripting and system configuration. They provide a clean, flexible way to pass configuration data to scripts and applications without hardcoding values. By understanding the difference between shell variables and environment variables, mastering parameter expansion, and following best practices like quoting variables, validating required values, and storing secrets securely, you can write robust and maintainable Zsh scripts. Whether you are building automation tools, configuring your development environment, or writing production scripts, a solid grasp of environment variables in Zsh will make you a more effective and efficient developer.

— Ad —

Google AdSense will appear here after approval

← Back to all articles