A Month With Bash — Part 2: Expansions
Continuing from where I left off, the next thing I learned was special parameters in bash:
"$*"$#$?$@$N$-$0
Another important concept I picked up is how bash executes shell scripts. Bash is one of those languages that interprets each line as it goes — but it doesn't stop if a line fails. It continues on unless you explicitly set set -o pipefail (or -e, depending on what you want it to catch).
Generally, the procedure looks like this:
Tokenizing: splitting the line into tokens, usually split using the IFS value.
Brace expansion: a mechanism by which arbitrary strings can be generated.
echo file{1,2,3}.txt
## output: file1.txt file2.txt file3.txt
Bash preserves the order from left to right.
Tilde expansion: this is where expansion of special symbols takes place.
-
~represents theHOMEbuilt-in variable -
~+representsPWD, the current working directory - and others
DIR=~/Desktop # this is $HOME/Desktop
echo "$DIR"
Parameter expansion: introduced with the $ symbol.
# ${} — the braces can be omitted for normal variables but not for array-type variables
Command substitution: very important — it lets you assign the output of a command to a variable, and use commands inside if and for statements. Done with $(command to execute).
week_name="$(date +%A)" # gets the current day of the week
echo "$week_name"
Generally, $() spawns a new shell instance, so it's advisable to avoid it where possible, for latency reasons.
Arithmetic expansion: just from the name, this allows evaluation of arithmetic expressions and substitution of the result. It starts with $(( expression )). There are some rules — bash doesn't support floating point arithmetic natively, so you'd reach for bc if you need it. I won't go deep into that here since this isn't a full bash tutorial.
Here's a simple BMI calculator I wrote while practicing this:
#!/usr/bin/env bash
# script calculates user's BMI and gives a recommendation
set -euo pipefail
# ---------------- read parameters ----------------
weight="$1"
height="$2"
bmi=$(bc -q <<< "scale=2; $weight / ($height^2)")
# ---------------- test ----------------
if (( $(bc -q <<< "$bmi < 18.5") )); then
echo "UNDERWEIGHT"
elif (( $(bc -q <<< "$bmi < 25") )); then
echo "normal weight"
elif (( $(bc -q <<< "$bmi < 30") )); then
echo "overweight"
else
echo "obese"
fi
Process substitution: this saves the output of a process into a temporary file-like descriptor under /dev/fd/N. It lets you feed command output into programs that expect a file as input. Done with >(command) and <(command).
#!/usr/bin/env bash
# script demonstrates process substitution
# process substitution is when you want to use the output of a command
# as the input to a command that needs files as parameters
set -euo pipefail
# gets the difference between two texts without making temp files
echo "$(diff -p <(echo "hello mr beast") <(echo "hello mr beast."))"
# note: <(cmd) uses the output of a command as a file
# >(cmd) takes input from a program and feeds it to the command as a file
Word splitting and globbing: occurs after expansion, only on unquoted values.
Next up: regex, grep/sed/awk, and moving into building actual projects.
Top comments (0)