2.5. Walkthrough: Substitutions & Scripting in Bash

In this walkthrough we will explore the concept of substitution. In Bash, and PowerShell, substitution is one of the most common features used when working from the command-line or writing scripts.

A substitution, as the name implies, will substitute a value into another expression. The substituted value can be a reference to a variable or an embedded command that will be executed before substituting its output.

2.5.1. Variables

Earlier we discussed environment variables – variables that affect every command and script executed in the Shell. You can also have variables that are only available within a script.

In relatable programming terms, script variables are scoped to the script whereas environment variables are globally scoped to the whole Shell.

2.5.1.1. Defining variables

When defining variables in a script the convention is to use lowercase letters and separate words with underscores (_). Environment variables are written in all capital letters so they are easy to distinguish from script variables.

Because Bash does not have any data types a variable is simple to declare and assign. First you define the name of the variable followed by an assignment symbol (=) and the value of it on the right side.

Note

Spaces are use to delimit, or distinguish, different parts of a command called tokens. Token splitting is what allows Bash to see a command along with its arguments and options as individual units to be evaluated – each separated by a space.

When defining a variable there can be no spaces between the variable declaration and assignment:

Linux/Bash
# correct: no spaces
variable_name=value

# wrong: spaces between declaration and assignment
variable_name = value

When the value of a variable does not have any spaces it can be written as shown above. When you need to have spaces you can put single-quotes (‘’) around the value. These serve to group the whole string value together including the spaces:

Linux/Bash
variable_name='a longer value'

2.5.1.2. Referencing variables

Once a variable has been defined (either in the script or a global environment variable) it can be referenced by prefixing a $ symbol before it:

Linux/Bash
# define the variable
my_variable='hello world'

# reference the script variable
$my_variable

# reference a global variable
$PATH

2.5.2. Variable Substitution

Referencing a variable is straightforward. But in most cases this process is done inside of a command, referencing in the open as we have done above has no effect. For this behavior Bash has a mechanism called variable substitution.

For example, consider this simple script that creates and moves a directory using variables to hold the paths. Above each command is a comment showing what the command looks like when its variables have been substituted.

Create a directory called bash-examples in your home (~) directory and open a new file called variable-substitution.sh within it. You can use any editor you would like to paste in the contents below.

~/bash-examples/variable-substitution.sh
target_path=/tmp/dir-name

# ~ is a shorthand for /home/<username>
destination_path=~/dir-name

# mkdir /tmp/dirname
mkdir "$target_path"

# mv /tmp/dir-name ~/dir-name
mv "$target_path" "$destination_path"

# $HOME is an environment variable with a value of /home/<username>
# ls /home/<username>
ls "$HOME"

You should now have a file with the path ~/bash-examples/variable-substitution.sh that you can execute using bash as the interpreter:

Linux/Bash
$ bash ~/bash-examples/variable-substitution.sh

2.5.2.1. Quoting syntax

You likely noticed that the variables are contained in double-quotes ("") when used in commands. This is a best practice when working with substitutions as it can prevent unintended behavior caused by spaces or special characters. This is especially true when scripts receive user input which, as you now know, should never be trusted!

Note

You can read more about the behavior of escape characters and single and double quotes in this article. If it goes over your head it’s okay, just follow the best practice to stay safe and come back to understanding the why later.

2.5.3. Command Substitution

Command substitution, as the name implies, is just like variable substitution but for commands. It allows you to execute a command within another command. We will see many examples of its usage throughout this course but for now consider the basic aspects of it.

We will refer to command substitutions interchangeably with in-line executions as they are evaluations performed in the line of a command being executed. An in-line execution allows you to embed a command within another like this:

Linux/Bash
$ command $(sub-command)

In this example the sub-command will first be evaluated (in-line), then the main command will be evaluated. When the command is evaluated the output of $(sub-command) will substituted in as its argument.

You can treat in-line executions as you would any other command, with arguments and options. The only difference is that, like all programming languages, commands are evaluated from the inside out. Any in-line executions will first be evaluated before stepping outwards and substituting their output.

Consider a more complicated example to understand how it works:

Linux/Bash
$ command $(sub-command $(sub-sub-command))

This command would be evaluated in the following order:

  1. innermost level: $(sub-sub-command)
  2. next level: $(sub-command <output of sub-sub-command>)
  3. outermost level: command <output of sub-command>

This is particularly useful in scripts when you want to capture the output of a command in a variable for reuse elsewhere in a script.

2.5.4. Substitutions Practice in a Script

Because substitution is a more advanced topic we will return to it later in a context that necessitates it. For now consider the following contrived example where we store our “history” of working directories (WD) in variables to navigate around them.

In your bash-examples directory create another file called command-substitution.sh and paste in the following contents. We will use the echo command to print out our CWD throughout the script:

~/bash-examples/command-substitution.sh
# in-line execution in a string message
echo "CWD is: $(pwd)"

# in-line execution to assign the value
first_wd=$(pwd)

cd /tmp
echo "CWD is: $(pwd)"

second_wd=$(pwd)

cd /usr/bin
echo "CWD is: $(pwd)"

third_wd=$(pwd)

# return to the first
echo "returning to first WD"
cd "$first_wd"
echo "CWD is: $(pwd)"

# jump to the second
echo "jumping to second WD"
cd "$second_wd"
echo "CWD is: $(pwd)"

Then execute the script the same way as before:

Linux/Bash
$ bash ~/bash-examples/command-substitution.sh

Note

As a good programmer you are likely miffed by the copy and pasting of an identical statement. Although we won’t be getting into Bash functions you should be able to make sense of them.

Here is a cleaner approach to help you sleep at night!

~/bash-examples/command-substitution.sh
print_cwd() {
   echo "CWD is: $(pwd)"
}

print_cwd

first_wd=$(pwd)

cd /tmp
print_cwd

second_wd=$(pwd)

cd /usr/bin
print_cwd

third_wd=$(pwd)

# return to the first
echo "returning to first WD"
cd "$first_wd"
print_cwd

# jump to the second
echo "jumping to second WD"
cd "$second_wd"
print_cwd

2.5.5. Learn More

This has been an introduction to the practical fundamentals of Bash. You are not expected to have memorized any of it by any means. Feel free to refer back to this article throughout the course to refresh your memory.

Learning Bash takes a lot of time. We covered a lot of ground today and will be revisiting the fundamentals regularly until they become second nature.

If you want to learn more advanced usage this Bash cheat-sheet from DevHints will get you up to speed quickly. DevHints is an open source site filled with quick-reference guides for many languages and frameworks. All of their guides are written and edited through contributions from the open source community.