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.

Leave a Reply

Your email address will not be published. Required fields are marked *