Shell Scripting Tricks
Understand those secret shell script tricks with this quick and easy tutorial.

Introduction
I'm sure you've seen some strange stuff when looking at shell scripts. This tutorial is a just a quick overview of some those secret tricks that are hard to figure out what they are with just a Google search. I primarily use the BASH, ZSH and ASH shells, so these are things that I believe work with those; I won't specifiy whether these things work in other shells or whether they are POSIX.
Terminology
Here is some basic shell terminology you need to know.
-
Parameter
A parameter is essentially just a variable, except BASH distinguishes between Positional Parameters (the parameters supplied to the script on the command line, like: ./my_script.sh these are positional parameters & Variable Parameters (the parameters defined inside the script, like: my_variable_parameter='hello';
-
Word
A word is just a string.
-
Pattern
A regex matching pattern.
What This Tutorial Covers
What This Tutorial Covers
- Tests
- Redirection & Piping
- Parameter & Other Expansions
- Subshells
- The Set Command
- Special Variables
What You Need For This Tutorial
What You Need For This Tutorial
A terminal if you want to play around with these tricks.
Tests
By tests, I'm just referring to how 'if' evaluates expressions. The way shell scripts evaluate expressions is particularly unique.
- Use the 'test' command to evaluate an expression.
if test "hi" = "hi"; then echo "there"; fi
if [ "hi" = "hi" ]; then echo "there"; fi
if [[ "hi" =~ "^h" ]]; then echo "there"; fi
Output Redirection & Piping
File descriptors are just numbers that are shortcuts to file streams. You always start with three, stdin (0), stdout (1), stderr (2). You can use redirection to move these around.
- Redirect stderr (2) to stdout (1). On the left of the '>' chevron, you just need the number of the file descriptor. On the right of the '>' chevron you need to prepend the file descriptor number with an ampersand.
2>&1
3< readFile.txt
4> writeFile.txt
grep "hello" <&3 >&4
3<&-
3<> myFile.txt
Redirection is just moving output from one file stream to another file stream, while Pipe is the redirection of the output from one command to another command.
- Run a command and redirect stdout to a file. Without a number, the '>' chevron defaults to redirecting stdout (1). Commands are written to pull input from stdin and push output to either stdout or stderr. So what's really happening here is that the 'echo' command is printing 'hello there' to stdout and then we are redirecting whatever stdout receives to the file.
echo 'hello there' > 'my file.txt'
rmdir some_dir_that_does_not_exist 2> 'my file.txt'
echo 'hello there' >> 'my file.txt'
cat <<EOF
hi
there
how's it going
EOF
cut -d' ' -f2 <<< 'why hello there'
echo 'hello there' | cut -d' ' -f1
You can also pipe stderr using a special pipe syntax. Usually only available in later shell versions.
rmdir some_dir_that_does_not_exist |& cut -d':' -f3
Shell Expansions & Subshells
Expansions are when some code is converted to something else when evaluated. Here I'll go over several types of expansions (except parameter expansion, which is described in the next section below).
Filename Expansion, otherwise known as Globbing, are some characters you can use to expand a filename into multiple files.
- Remove all files that start with 'hello'
rm hello*
rm hell?.txt
rm hell[ao].txt
Tilde & Dash Expansion
- Change directory to current user's home
cd ~
cd ~root
cd -
Brace Expansion
- Run the mkdir command twice to create both directories: /var/log & /var/tmp
mkdir /var/{log,tmp}
Subshells, Command Substitution, & Parallel Processing.
- Run the echo command in a child process. This is known as a Subshell.
(echo 'hello there')
result=$(echo 'hello there')
(echo 'hello there') &
(sleep 1 && echo 'hello there') &
(echo 'how are you today?') &
wait
echo 'i am fine, thank you'
Arithmetic Expressions
- Arithmetic expressions must be run in double parentheses.
((2 + 2))
result=$((2 + 2))
Parameter Expansion
There are many tricks for condensing logic when using parameters. Here are the most common ones I have seen.
- Use the parameter's value
variable=${parameter}
variable=${parameter:-'word'}
variable=${parameter:+word}
${parameter:='word'}
${parameter:?'error message'}
Offsets can be negative (offset from end of word) and lengths can be negative (number of chars going backwards)
${parameter:offset}
${parameter:offset:length}
A single # means remove the shortest pattern match, and a double ## means remove the longest pattern match.
${parameter#pattern}
${parameter##pattern}
${parameter%pattern}
${parameter%%pattern}
${parameter/pattern/word}
Set
Set is a built in shell command. It's used to define the functionality around parameters. I'm going to go over a few of the more widely used commands.
# Set new positional parameters (they replace the old ones, they don't append)
set -- these are new positional parameters
# If a command in your script fails, exit the script
set -e
# You can always change the '-' to a '+' to turn the feature off
set +e
# If you try to expand a variable that does exist, exit the script
set -u
# If a command in a series of piped commands fail,
# return its exit status instead of the exit status of the last command
set -o pipefail
# Print the commands that are run prepended by a '+' sign,
# followed by their output. Useful for debugging.
set -x
# Do not allow redirection to overwrite existing files.
set -C
There is of course, also the unset built in command. But it is much simpler. You use it just to unset variables
unset myVariable
Special Variables
When you start a script in shell, there are certain predefined special variables.
# The name of the script or command
$0
# The arguments passed to the script
$1 $2 $3
# All of the arguments passed to the script
$*
# The entire command line, or in other words,
# the script name and all of its arguments
$@
# The number of arguments passed to the script
$#
# The options (flags) the shell has on to set how it behaves
$-
# Exit value of the last command
$?
# Process ID of the last command
$!
Done!
That's the weird looking stuff I know about in shell scripting.