Introduction to Bash Variables
Variables in Bash are fundamental containers for storing and manipulating data. They allow you to store values, reference them throughout your scripts, and perform operations on them. Understanding Bash variables is crucial for writing effective shell scripts.
Key Concepts
- No Data Types: All variables are stored as strings
- Case-Sensitive:
$VARand$varare different - Scope: Variables can be global or local
- Special Variables: Positional parameters and system variables
- Expansion: Variables are expanded with
$prefix
1. Basic Variable Declaration
Simple Variable Assignment
#!/bin/bash
# Basic assignment (no spaces around =)
name="John Doe"
age=25
city="New York"
# Accessing variables
echo "$name is $age years old and lives in $city"
# Output: John Doe is 25 years old and lives in New York
# Variables without $ when assigning, with $ when accessing
greeting="Hello"
echo $greeting World
echo "${greeting}, World!" # Curly braces for clarity
Variable Naming Rules
#!/bin/bash # Valid variable names name="John" NAME="John" # Different from 'name' _name="John" # Starting with underscore name123="John" # Numbers allowed after first character long_variable_name="John" # Underscores for readability # Invalid variable names # 123name="John" # Cannot start with number # my-name="John" # Hyphen not allowed # my name="John" # Space not allowed # $name="John" # $ not allowed in assignment # Best practices # Use lowercase for local variables # Use UPPERCASE for environment variables and constants local_var="value" readonly MAX_COUNT=100 export GLOBAL_CONFIG="/etc/app/config"
2. Variable Types and Usage
String Variables
#!/bin/bash # String assignment name="John Doe" empty_string="" space_string=" " # String with a space # String concatenation first="John" last="Doe" full="$first $last" echo "$full" # John Doe # Quotes matter var1=Hello World # Error: World treated as command var2="Hello World" # Correct var3='Hello World' # Correct (literal) # Single vs Double quotes name="John" echo "Hello $name" # Hello John (variable expanded) echo 'Hello $name' # Hello $name (literal) # Escaping quotes message="He said: \"Hello\"" echo "$message" # Multi-line strings multiline="Line 1 Line 2 Line 3" echo "$multiline"
Numeric Variables (All strings, but can be used in arithmetic)
#!/bin/bash # Numbers are stored as strings count="10" echo "$count" # Arithmetic expansion sum=$((5 + 3)) echo "Sum: $sum" # Sum: 8 # Different arithmetic operations a=10 b=3 echo "Addition: $((a + b))" # 13 echo "Subtraction: $((a - b))" # 7 echo "Multiplication: $((a * b))" # 30 echo "Division: $((a / b))" # 3 (integer division) echo "Modulus: $((a % b))" # 1 echo "Power: $((a ** 2))" # 100 # Using let for arithmetic let "result = a + b" echo "Result: $result" # Result: 13 # Increment/decrement counter=0 ((counter++)) echo "$counter" # 1 ((counter--)) echo "$counter" # 0 ((counter+=5)) echo "$counter" # 5
Arrays
#!/bin/bash
# Indexed arrays
fruits=("apple" "banana" "orange")
numbers=(1 2 3 4 5)
mixed=("hello" 42 "world" 3.14)
# Accessing array elements
echo "${fruits[0]}" # apple
echo "${fruits[1]}" # banana
echo "${fruits[@]}" # all elements
echo "${#fruits[@]}" # array length
# Adding elements
fruits[3]="grape"
fruits+=("kiwi" "mango")
# Iterating over array
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Associative arrays (Bash 4+)
declare -A capitals
capitals["USA"]="Washington"
capitals["France"]="Paris"
capitals["Japan"]="Tokyo"
echo "${capitals["France"]}" # Paris
echo "${!capitals[@]}" # all keys
echo "${capitals[@]}" # all values
# Iterate over associative array
for country in "${!capitals[@]}"; do
echo "$country: ${capitals[$country]}"
done
3. Special Variables
Positional Parameters
#!/bin/bash # script.sh echo "Script name: $0" echo "First argument: $1" echo "Second argument: $2" echo "Third argument: $3" echo "All arguments: $@" echo "Number of arguments: $#" echo "All arguments (as single string): $*" # Shift through arguments echo "Processing arguments:" while [ $# -gt 0 ]; do echo "Argument: $1" shift done # Example usage # ./script.sh arg1 arg2 arg3
Special Shell Variables
#!/bin/bash # Process ID echo "Current shell PID: $$" echo "Last background process PID: $!" # Exit status echo "Last command exit status: $?" # Shell options echo "Shell options: $-" # Previous working directory cd /tmp cd ~ echo "Previous directory: $OLDPWD" # Current working directory echo "Current directory: $PWD" # Random number echo "Random number: $RANDOM" echo "Another random: $RANDOM" # Line number echo "Current line number: $LINENO"
Environment Variables
#!/bin/bash # Common environment variables echo "Home directory: $HOME" echo "User: $USER" echo "Username: $LOGNAME" echo "Shell: $SHELL" echo "Path: $PATH" echo "Terminal: $TERM" echo "Language: $LANG" echo "Hostname: $HOSTNAME" echo "Display: $DISPLAY" # Setting environment variables export MY_APP_CONFIG="/etc/myapp/config" export DATABASE_URL="postgresql://localhost/mydb" # Environment for a single command PATH="/custom/bin:$PATH" command_name # Listing all environment variables env printenv declare -p # Show all variables
4. Variable Operations
String Operations
#!/bin/bash
# String length
str="Hello World"
echo "Length: ${#str}" # 11
# Substring extraction
echo "${str:6}" # World (from position 6)
echo "${str:6:3}" # Wor (3 chars from position 6)
echo "${str: -5}" # World (last 5 chars)
echo "${str: -5:2}" # Wo (2 chars from 5 from end)
# Pattern removal
filename="document.txt"
echo "${filename%.txt}" # document (remove suffix)
echo "${filename%.*}" # document (remove shortest suffix)
echo "${filename%%.*}" # document (remove longest suffix)
path="/home/user/file.txt"
echo "${path#*/}" # home/user/file.txt (remove shortest prefix)
echo "${path##*/}" # file.txt (remove longest prefix)
# Search and replace
text="Hello Hello World"
echo "${text/Hello/Hi}" # Hi Hello World (first match)
echo "${text//Hello/Hi}" # Hi Hi World (all matches)
echo "${text/#Hello/Hi}" # Hi Hello World (match at start)
echo "${text/%World/All}" # Hello Hello All (match at end)
# Default values
unset var
echo "${var:-default}" # default (if unset or empty)
echo "${var-default}" # default (only if unset)
echo "${var:=default}" # assign default and return
echo "$var" # default (now assigned)
# Error if unset
echo "${var:?Error message}" # prints error if unset or empty
Case Modification (Bash 4+)
#!/bin/bash
# Uppercase/lowercase
name="john DOE"
echo "${name^}" # John DOE (first letter uppercase)
echo "${name^^}" # JOHN DOE (all uppercase)
echo "${name,}" # john DOE (first letter lowercase)
echo "${name,,}" # john doe (all lowercase)
# Case transformation with patterns
text="hello-world"
echo "${text^}" # Hello-world
echo "${text^^}" # HELLO-WORLD
echo "${text^^[a-z]}" # HELLO-WORLD
echo "${text^^[a]}" # hello-world (only a)
5. Variable Scope
Local vs Global Variables
#!/bin/bash
# Global variable
global_var="I'm global"
function test_scope() {
# Local variable
local local_var="I'm local"
echo "Inside function: $global_var"
echo "Inside function: $local_var"
# Modify global
global_var="Modified global"
# Create new global inside function
new_global="Created in function"
}
test_scope
echo "Outside: $global_var" # Modified global
echo "Outside: $local_var" # Empty (not accessible)
echo "Outside: $new_global" # Created in function
# Function with local variables only
function calculate() {
local result=$(( $1 + $2 ))
echo "$result"
}
sum=$(calculate 5 3)
echo "Sum: $sum"
Exporting Variables
#!/bin/bash # Export variable to child processes export CONFIG_FILE="/etc/myapp/config" export DEBUG=true # Export after assignment DATABASE_URL="postgresql://localhost/db" export DATABASE_URL # Export multiple variables export VAR1="value1" VAR2="value2" VAR3="value3" # Child script (child.sh) cat > child.sh << 'EOF' #!/bin/bash echo "Config file: $CONFIG_FILE" echo "Debug mode: $DEBUG" EOF chmod +x child.sh ./child.sh # Unset variable unset DATABASE_URL unset VAR1 VAR2 VAR3
6. Advanced Variable Techniques
Indirect Expansion
#!/bin/bash
# Variable indirection
fruit_apple="Apple is red"
fruit_banana="Banana is yellow"
fruit="apple"
echo "${fruit_$fruit}" # Error
echo "${fruit_apple}" # Apple is red
# Using indirection
chosen="apple"
ref="fruit_$chosen"
echo "${!ref}" # Apple is red (indirect expansion)
# Practical example
config_user="admin"
config_pass="secret"
config_host="localhost"
for setting in user pass host; do
ref="config_$setting"
echo "Setting $setting: ${!ref}"
done
Variable Attributes
#!/bin/bash
# Readonly variables
readonly PI=3.14159
PI=3.14 # Error: cannot modify readonly
# Integer attribute
declare -i number
number=10
number="5" # Works (string converted to integer)
number="hello" # Becomes 0
echo "$number"
# Array attributes
declare -a indexed_array
declare -A associative_array
# Lowercase/Uppercase attributes
declare -l lowercase="HELLO" # Automatically lowercase
echo "$lowercase" # hello
declare -u uppercase="hello" # Automatically uppercase
echo "$uppercase" # HELLO
# Read from file
declare -a lines
mapfile -t lines < file.txt
echo "${lines[0]}"
Dynamic Variable Names
#!/bin/bash
# Creating dynamic variable names
for i in {1..5}; do
declare "var_$i=$i"
done
echo "$var_1" # 1
echo "$var_5" # 5
# Using eval (be careful!)
name="value"
eval "$name='Hello World'"
echo "$value" # Hello World
# Better: using declare
for i in {1..3}; do
declare "fruit_$i=$(echo "fruit$i")"
done
# List variables with pattern
echo "${!fruit_@}" # fruit_1 fruit_2 fruit_3
echo "${!fruit_*}" # fruit_1 fruit_2 fruit_3
7. Variable Expansion and Quoting
Quote Types
#!/bin/bash # Double quotes (allow expansion) name="John" echo "Hello $name" # Hello John echo "Hello \$name" # Hello $name (escaped) # Single quotes (literal) echo 'Hello $name' # Hello $name echo 'Hello "World"' # Hello "World" # No quotes (word splitting, globbing) echo Hello World # Hello World (multiple spaces collapsed) echo * # Lists files in current directory # ANSI-C quoting ($'...') echo $'Hello\nWorld' # Newline interpretation echo $'Tab\there' # Tab character echo $'Bell\a' # Bell character echo $'Quote: \'' # Single quote
Word Splitting
#!/bin/bash # IFS (Internal Field Separator) data="apple:banana:orange" # Default IFS (space, tab, newline) for item in $data; do echo "$item" # Splits by spaces (none here) - whole string done # Change IFS IFS=":" for item in $data; do echo "$item" # Splits by colon: apple, banana, orange done # Reset IFS unset IFS # Preserve spaces with IFS IFS=$'\n' # Newline only # Word splitting examples list="one two three" set -- $list # Split into positional parameters echo "$1" # one echo "$2" # two echo "$3" # three # Prevent word splitting list="one two three" set -- "$list" # No splitting echo "$1" # one two three
8. Command Substitution
Basic Command Substitution
#!/bin/bash # Old style (backticks) files=`ls` echo "Files: $files" # Modern style ($()) files=$(ls) echo "Files: $files" # Nesting command substitution output=$(echo "Date: $(date)") echo "$output" # Complex examples current_date=$(date +%Y-%m-%d) echo "Today: $current_date" lines=$(wc -l < file.txt) echo "Lines: $lines" # Using in arithmetic count=$(grep -c "pattern" file.txt) echo "Matches: $((count * 2))" # Multi-line output files_list=$(ls -la) echo "$files_list" # Quotes preserve newlines
Process Substitution
#!/bin/bash # Process substitution (Bash 4+) diff <(ls dir1) <(ls dir2) # Compare files while read line; do echo "Processed: $line" done < <(grep "pattern" file.txt) # Multiple process substitutions paste <(cut -f1 file1) <(cut -f2 file2) # Complex example while IFS= read -r line1 && IFS= read -r line2 <&3; do echo "Line1: $line1, Line2: $line2" done < <(command1) 3< <(command2)
9. Variable Attributes and Declare
Using declare/typeset
#!/bin/bash
# Declare variable with attributes
declare -r READONLY_VAR="Cannot change"
declare -i INTEGER=10
declare -a ARRAY=(1 2 3)
declare -A ASSOC=([key]=value)
declare -x EXPORTED_VAR="Visible to child processes"
declare -l LOWERCASE="HELLO"
declare -u UPPERCASE="hello"
# List variables
declare -p # Show all variables with attributes
declare -p PATH # Show PATH variable details
# Function-local variables
function test() {
declare -i local_int=5
echo "$local_int"
}
# Read-only array
declare -ra COLORS=("red" "green" "blue")
# COLORS[0]="yellow" # Error: readonly
# Export array (not directly possible)
# Workaround using string
declare -ax MY_ARRAY=(1 2 3) # Still won't export
Variable Attributes Examples
#!/bin/bash
# Integer arithmetic with declare -i
declare -i count
count=10
count+=5 # Addition, not concatenation
echo "$count" # 15
count="2 * 3"
echo "$count" # 6
# Lowercase/Uppercase with assignment
declare -l name="JOHN DOE"
echo "$name" # john doe
name="JANE SMITH"
echo "$name" # jane smith
declare -u code="abc123"
echo "$code" # ABC123
# Trace function calls
declare -t TRACE_VAR="track this"
set -o functrace
# Array attributes
declare -a numbers
numbers[5]=10
echo "${#numbers[@]}" # 1 (sparse array)
echo "${!numbers[@]}" # 5 (indexes)
10. Practical Examples
Configuration File Parser
#!/bin/bash
# Simple config parser
parse_config() {
local config_file="$1"
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ "$key" =~ ^#.*$ || -z "$key" ]] && continue
# Trim whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# Remove quotes if present
value="${value%\"}"
value="${value#\"}"
value="${value%\'}"
value="${value#\'}"
# Export as variable
export "$key=$value"
done < "$config_file"
}
# Example config file
cat > app.conf << 'EOF'
# Application configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DEBUG=true
MAX_CONNECTIONS=100
EOF
# Parse and use
parse_config app.conf
echo "Database: $DB_NAME on $DB_HOST:$DB_PORT"
echo "Debug mode: $DEBUG"
Safe Variable Handling
#!/bin/bash
# Safe variable access with defaults
get_env() {
local var_name="$1"
local default="${2:-}"
if [ -n "${!var_name}" ]; then
echo "${!var_name}"
else
echo "$default"
fi
}
# Validate variable is set
require_var() {
local var_name="$1"
local message="${2:-Required variable $var_name is not set}"
if [ -z "${!var_name}" ]; then
echo "ERROR: $message" >&2
exit 1
fi
}
# Use defaults
DB_HOST=$(get_env "DB_HOST" "localhost")
DB_PORT=$(get_env "DB_PORT" "5432")
# Require critical variables
require_var "API_KEY" "API key must be set"
# Safe array handling
safe_array_get() {
local array_name="$1"
local index="$2"
local default="${3:-}"
eval "value=\"\${${array_name}[$index]:-}\""
if [ -n "$value" ]; then
echo "$value"
else
echo "$default"
fi
}
Variable Scope Manager
#!/bin/bash
# Save and restore variable states
save_vars() {
local save_file="${1:-/tmp/vars_save}"
declare -p | grep -v "^declare -[aA]" > "$save_file"
}
restore_vars() {
local save_file="${1:-/tmp/vars_save}"
if [ -f "$save_file" ]; then
source "$save_file"
rm "$save_file"
fi
}
# Temporary variable changes
with_vars() {
local vars_file="/tmp/vars_$$"
save_vars "$vars_file"
# Set temporary variables
eval "$1"
# Run command
eval "${@:2}"
# Restore
restore_vars "$vars_file"
}
# Usage
with_vars "PATH=/custom/bin:\$PATH; DEBUG=1" "my_command --verbose"
Environment Manager
#!/bin/bash
# Environment setup/teardown
setup_environment() {
local env_file="$1"
# Save current environment
declare -p > "/tmp/env_before_$$"
# Load new environment
while IFS= read -r line; do
[[ "$line" =~ ^#.*$ || -z "$line" ]] && continue
export "$line"
done < "$env_file"
}
teardown_environment() {
# Restore previous environment
while IFS= read -r line; do
if [[ "$line" =~ ^declare\ -x\ ([^=]+)= ]]; then
var="${BASH_REMATCH[1]}"
unset "$var"
fi
done < "/tmp/env_before_$$"
# Reload saved variables
source "/tmp/env_before_$$"
rm "/tmp/env_before_$$"
}
# Environment profiles
profile_dev() {
export DB_HOST="localhost"
export DB_NAME="dev_db"
export DEBUG=1
}
profile_prod() {
export DB_HOST="prod.db.example.com"
export DB_NAME="prod_db"
export DEBUG=0
}
# Switch profile
use_profile() {
local profile="$1"
"profile_$profile"
}
11. Error Handling with Variables
Variable Validation
#!/bin/bash
# Validate variable types
validate_integer() {
local var_name="$1"
local value="${!var_name}"
if [[ ! "$value" =~ ^-?[0-9]+$ ]]; then
echo "Error: $var_name must be an integer" >&2
return 1
fi
}
validate_positive() {
local var_name="$1"
local value="${!var_name}"
if [[ ! "$value" =~ ^[0-9]+$ ]] || [ "$value" -le 0 ]; then
echo "Error: $var_name must be positive integer" >&2
return 1
fi
}
validate_email() {
local var_name="$1"
local value="${!var_name}"
if [[ ! "$value" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Error: $var_name must be valid email" >&2
return 1
fi
}
validate_ip() {
local var_name="$1"
local value="${!var_name}"
if [[ ! "$value" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "Error: $var_name must be valid IP" >&2
return 1
fi
# Validate each octet
IFS='.' read -r a b c d <<< "$value"
if [ "$a" -gt 255 ] || [ "$b" -gt 255 ] || [ "$c" -gt 255 ] || [ "$d" -gt 255 ]; then
echo "Error: $var_name contains invalid octet" >&2
return 1
fi
}
Defensive Programming
#!/bin/bash
# Set strict mode
set -euo pipefail
# Check required variables
required_vars=("DB_HOST" "DB_USER" "DB_PASS")
for var in "${required_vars[@]}"; do
if [ -z "${!var:-}" ]; then
echo "Error: $var is not set" >&2
exit 1
fi
done
# Use default values safely
: "${MAX_RETRIES:=3}"
: "${TIMEOUT:=30}"
# Prevent unset variable errors
set -u
echo "$UNDEFINED_VAR" # This will error
# Safer alternative
echo "${UNDEFINED_VAR:-}" # Empty string if unset
# Check if variable is set
if [ -n "${SOME_VAR+set}" ]; then
echo "SOME_VAR is set"
else
echo "SOME_VAR is not set"
fi
12. Performance and Best Practices
Variable Performance Tips
#!/bin/bash
# Use local variables in functions (faster)
function slow() {
for i in {1..1000}; do
global_var=$i # Global variable (slower)
done
}
function fast() {
local local_var
for i in {1..1000}; do
local_var=$i # Local variable (faster)
done
}
# Avoid unnecessary command substitution
# Slow
for i in $(seq 1 1000); do
# ...
done
# Fast
for ((i=1; i<=1000; i++)); do
# ...
done
# Use built-in operations instead of external commands
# Slow
len=$(echo -n "$string" | wc -c)
# Fast
len=${#string}
# Use arrays instead of string parsing
# Better
files=("$@")
for file in "${files[@]}"; do
echo "$file"
done
# Avoid eval when possible
# Dangerous
eval "var_$i='value'"
# Safer
declare "var_$i=value"
Best Practices
#!/bin/bash
# 1. Always quote variables
file="my file.txt"
cat "$file" # Good
cat $file # Bad (breaks with spaces)
# 2. Use descriptive names
user_input="data" # Good
ui="data" # Bad (too short)
path_to_config_file="/etc/app/config" # Good
p="/etc/app/config" # Bad (not descriptive)
# 3. Use readonly for constants
readonly MAX_CONNECTIONS=100
readonly PI=3.14159
# 4. Initialize variables
local var="" # Good
local var # Bad (may inherit previous value)
# 5. Use lowercase for local variables
local user_name="John" # Good
local USER_NAME="John" # Bad (looks like environment)
# 6. Use uppercase for exported variables
export APP_HOME="/opt/myapp" # Good
export app_home="/opt/myapp" # Bad (convention)
# 7. Check variables before use
if [ -n "${filename:-}" ]; then
process "$filename"
fi
# 8. Use default values
timeout=${TIMEOUT:-30}
debug=${DEBUG:-false}
# 9. Clean up sensitive variables
secret_key="supersecret"
process_data "$secret_key"
unset secret_key
# 10. Use arrays for lists
# Instead of: fruits="apple banana orange"
fruits=("apple" "banana" "orange")
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
13. Common Pitfalls and Solutions
Variable Pitfalls
#!/bin/bash
# Pitfall 1: Spaces in assignments
# Wrong
name = "John" # Error: name command not found
# Correct
name="John"
# Pitfall 2: Forgetting $ when reading
count=5
echo count # prints "count", not 5
echo "$count" # prints 5
# Pitfall 3: Word splitting
files="file1 file2 file3"
for file in $files; do # Works but fragile
echo "$file"
done
# Better
files=("file1" "file2" "file3")
for file in "${files[@]}"; do
echo "$file"
done
# Pitfall 4: Globbing with unquoted variables
pattern="*"
echo $pattern # Lists files in directory
echo "$pattern" # Prints *
# Pitfall 5: Environment inheritance
export VAR="value"
(export VAR="child"; echo "$VAR") # Prints "child"
echo "$VAR" # Prints "value" (unchanged)
# Pitfall 6: Array vs string
array=(1 2 3)
echo "$array" # Prints "1" (only first element)
echo "${array[@]}" # Prints "1 2 3"
# Pitfall 7: Unset variables in strict mode
set -u
echo "$UNDEFINED" # Error
# Safer
echo "${UNDEFINED:-}"
# Pitfall 8: Exporting arrays
export myarray=(1 2 3) # Doesn't work
# Workaround: export as string
export myarray="1 2 3"
# Pitfall 9: Variable scope in pipelines
count=0
echo -e "1\n2\n3" | while read line; do
((count++))
done
echo "$count" # Still 0 (subshell issue)
# Fix with process substitution
count=0
while read line; do
((count++))
done < <(echo -e "1\n2\n3")
echo "$count" # 3
# Pitfall 10: Using echo with flags
var="-n"
echo "$var" # Prints nothing (interpreted as echo flag)
printf "%s\n" "$var" # Safe alternative
14. Command Summary and Cheat Sheet
Variable Operations Quick Reference
# Assignment
name="value"
readonly constant="value"
export global="value"
declare -i integer=5
declare -a array=(1 2 3)
declare -A dict=([key]="value")
# Access
$name
${name}
${#name} # Length
${name:offset:length} # Substring
${name#pattern} # Remove shortest prefix
${name##pattern} # Remove longest prefix
${name%pattern} # Remove shortest suffix
${name%%pattern} # Remove longest suffix
${name/pattern/replacement} # Replace first
${name//pattern/replacement} # Replace all
${name^} # First character uppercase
${name^^} # All uppercase
${name,} # First character lowercase
${name,,} # All lowercase
# Default values
${var:-default} # Use default if unset/empty
${var-default} # Use default if unset
${var:=default} # Assign default if unset/empty
${var:=default} # Assign default if unset
${var:?error} # Error if unset/empty
${var?error} # Error if unset
# Indirect expansion
${!var} # Indirect reference
# Array operations
${array[@]} # All elements
${!array[@]} # Array indices
${#array[@]} # Array length
array+=("new") # Append
unset array[2] # Remove element
# Special variables
$0 # Script name
$1, $2... # Positional parameters
$@ # All arguments
$* # All arguments (as string)
$# # Number of arguments
$? # Exit status
$$ # Process ID
$! # Last background PID
$- # Shell options
Common Patterns
# Safe variable access
: "${VAR:=default}" # Set default if unset
: "${VAR:?Required}" # Error if unset
# Variable testing
[ -n "$VAR" ] # Not empty
[ -z "$VAR" ] # Empty
[ "${VAR+set}" = "set" ] # Is set (even if empty)
# Variable transformation
lower=$(echo "$VAR" | tr '[:upper:]' '[:lower:]')
upper=$(echo "$VAR" | tr '[:lower:]' '[:upper:]')
trimmed=$(echo "$VAR" | xargs)
# Loops with variables
for var in "${list[@]}"; do
echo "$var"
done
while IFS= read -r line; do
echo "$line"
done < file.txt
Conclusion
Bash variables are essential for effective shell scripting:
Key Takeaways
- No types - all variables are strings, but can be used numerically with arithmetic
- Case-sensitive - naming conventions matter
- Scope - understand local vs global vs environment
- Expansion -
${}provides powerful manipulation - Arrays - indexed and associative for complex data
- Special variables - positional parameters and system info
Best Practices Summary
| Practice | Example |
|---|---|
| Quote variables | "$var" |
| Use local in functions | local var |
| Use uppercase for constants | readonly MAX=100 |
| Use lowercase for locals | local count |
| Use arrays for lists | files=("$@") |
| Check variables before use | [ -n "$var" ] |
| Set defaults | : "${var:=default}" |
| Clean sensitive data | unset password |
Common Commands Summary
# Variable operations declare # Set attributes export # Make available to children local # Function-local readonly # Make read-only unset # Remove variable set # Show all variables/env # Information env # Show environment printenv # Print environment echo # Display values printf # Formatted output
Mastering Bash variables is fundamental to writing robust, maintainable shell scripts. They provide the foundation for data manipulation, configuration management, and program flow control in the Unix/Linux environment.