While not strictly redirection in the Linux sense, command substitution in our eyes is a form of functional redirection: you use the output of a command as an argument to another command. If we needed to use output as input for the next command, we'd use a pipe (as we'll see in a few pages), but sometimes we just need that output at a very specific location in our command.
This is where command substitution is used. We've already seen command substitution in some of our scripts: cd $(dirname $0). Simply put, this does something like cd to the result of dirname $0.
dirname $0 gives back the directory where the script is located (since $0 is the fully-qualified path of the script), so when we use this with scripts, we'll make sure all operations are always carried out relative to the directory where the script is located.
If we did not have command substitution, we'd need to store the output somewhere before we could use it again:
dirname $0 > directory-file
cd < directory-file
rm directory-file
While this sometimes works, there are some pitfalls here:
- You need to write a file somewhere where you have write permissions
- You need to clean up the file after the cd
- You need to make sure the file does not conflict with other scripts
To cut a long story short, this is a far from ideal solution, and best avoided. And since Bash supplies command substitution, there is no real drawback to using it. As we've seen, the command substitution in cd $(dirname $0) handles this for us, without the need for us to track files or variables or any other complicated constructions.
Command substitution is actually used quite a lot in Bash scripting. Take a look at the following example, in which we use command substitution to instantiate and populate a variable:
reader@ubuntu:~/scripts/chapter_12$ vim simple-password-generator.sh
reader@ubuntu:~/scripts/chapter_12$ cat simple-password-generator.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-11-10
# Description: Use command substitution with a variable.
# Usage: ./simple-password-generator.sh
#####################################
# Write a random string to a variable using command substitution.
random_password=$(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 20)
echo "Your random password is: ${random_password}"
reader@ubuntu:~/scripts/chapter_12$ bash simple-password-generator.sh
Your random password is: T3noJ3Udf8a2eQbqPiad
reader@ubuntu:~/scripts/chapter_12$ bash simple-password-generator.sh
Your random password is: wu3zpsrusT5zyvbTxJSn
For this example, we reused the logic in our earlier password-generator.sh script. This time, we do not give the user the option to supply a length; we keep it simple and assume a length of 20 (which is, at least in 2018, a pretty good length for a password).
We use command substitution to write the result (the random password) to a variable, which we then echo to the user.
We actually could have done this in a single line:
reader@ubuntu:~/scripts/chapter_12$ echo "Your random password is: $(tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 20)"
Your random password is: REzCOa11pA2846fvxsa
However, as we have discussed many times by now, readability counts (still!). We feel that first writing to a variable with a descriptive name, before we actually use it, increases the readability of the script.
Furthermore, if we wanted to use the same random value more than once, we need a variable anyway. So in this case, the extra verbosity in our script helps us and is desirable.