Mastering Command Substitution In Bash For Variable Assignment
Getting Started with Command Substitution
Definition and Basic Syntax of Command Substitution in Bash
Command substitution in Bash allows you to assign the output of a command to a variable or pass it as an argument to another command. It substitutes the command with its output. The basic syntax for command substitution is:
$(command) `command`
The $(command) format is preferred as it is easier to nest and more readable. The backticks “ are legacy syntax. Inside the $() or backticks, you can execute a command and anything printed to standard output will be captured and substituted.
When and Why to Use Command Substitution for Variable Assignment
Reasons you may want to use command substitution include:
- Save output of a command to a variable for later use in scripts
- Pass output of a command as argument to another command
- Conditional assignment based on command exit code
- Concatenate output of multiple commands
- Process substitutions before passing data to commands
Overall, command substitution is useful to assign and reuse output from CLI programs and scripts.
Examples of Common Use Cases
Some examples uses of command substitution:
# Save current date to a variable NOW=$(date +%F) # Get filename without extensions NAME=$(basename file.txt .txt) # Check homepage return code HTTP_STATUS=$(curl -Is example.com | head -1 | cut -d' ' -f2) # Conditional variable assignment [[ $HTTP_STATUS -eq 200 ]] && UP=1 || UP=0
Capturing Command Output
Using $(command) for Capturing Standard Output
The preferred $(command) syntax captures standard output stream. This allows saving informational output of a command to a variable.
NOW=$(date +%F)
This assigns date in YYYY-MM-DD format to $NOW variable.
Redirecting Standard Error with $(command 2>&1)
To capture both standard output and standard error streams use:
OUTPUT=$(command 2>&1)
This redirects STDERR to STDOUT allowing the substitution to save both streams to $OUTPUT.
Assigning Multi-line Output to a Variable
Command substitutions can save multi-line output. For example, capturing raw JSON output:
DATA=$(curl https://example.com/api/data) echo "$DATA"
This will assign full JSON dataset to $DATA variable.
Examples and Sample Code
More examples:
# Multi-line assignment SERVERS=$(cat /etc/hosts | grep server) # Capture errors LOG=$(bash script.sh 2>&1 | tee reports/log.txt) # Nest command in a subshell DATA=$( (curl api.com || curl fallback.com) 2>&1)
Nested Substitutions and Chained Commands
Nested Substitutions with $($(command))
We can nest command substitutions:
$(echo $(date))
The $(date)
inner substitution is evaluated first, then the outer substitution runs echo
on the result.
Chaining Multiple Commands with Pipes
Piped commands let you perform multiple operations:
OUTPUT=$(ps aux | grep [f]irefox | awk '{print $2}')
This chains ps
, grep
, and awk
together passing data between them.
Avoiding Errors and Edge Cases
When chaining commands, handle errors and validate assumptions:
COUNT=$(ps aux | grep "[f]irefox" 2>&1 > /dev/null | wc -l) || COUNT=0
This avoids a broken pipe error if firefox not running and sets $COUNT to 0.
Use Cases and Coding Examples
# Nested date formatting TODAY=$(date "+%Y%m%d-$(date +%A)") # Get memory usage for current PID MEM=$(ps --no-headers -o rss -p $$) # Random number b/w two numbers RND=$((RANDOM%($2-$1+1)+$1))
Best Practices
Quoting and Escaping Special Characters
Always quote command substitutions to avoid splitting and glob expansions:
FILES="${HOME}/docs/*.txt" wc -l $(find "${FILES}")
Also escape special characters interpreted by the shell like $
, backticks etc.
Handling Exit Statuses and Return Codes
Check return code to avoid errors:
if ! OUTPUT=$(cmd 2>&1); then echo "Error: $?" >&2 fi
Security Considerations
Validate user input before substituting to prevent code injection:
read -r filename [[ $filename =~ ^[a-zA-Z0-9_/.-]+$ ]] || exit 1 cat "$filename" 2>/dev/null
Performance and Efficiency Tips
Avoid unnecessary commands substitutions:
# Inefficient DIR=$(pwd) # Better DIR=$PWD
Also avoid excessive use in loops to improve speed.
Advanced Techniques
Conditional Assignment and If Statements
if [[ $(service apache status) == *"running"* ]]; then STATE="Running" else STATE="Not running" fi
Here command substitution is used to check service status inside an if statement.
Loops for Iterating Over Output
files=$(ls) for f in $files; do gzip "$f" done
This iterates over directory listing to gzip compress files.
Working with JSON, XML, CSV Output
Process structured data using substitutions:
data=$(curl api.com/data.json) value=$(echo "$data" | jq .user.id)
Extract relevant values using jq
, xpath
, csvtool
etc.
Integrating with External Commands
Extend functionality by piping to external programs:
reports=$(cmd | pandoc -f html -t pdf)
The possibilities are endless when combining command substitutions with the power of Linux utilities.