When you first hear of a Bash command list you may think it is a literal list of commands. Of course, that is one meaning, but in this article we are going to be discussing the concept of Bash command lists. You may have already heard of pipelines, which are a a sequence of one or more commands in which the shell uses output of one command as input for the next command. Similar to a pipeline a list is a sequence of commands, but the output it not piped. Instead of the output being piped to the next command, the shell uses different control operators to run the commands asynchronous, sequentially or use logic like AND and OR.

Asynchronous and Sequential Commands

Run Linux Commands Sequentially

The most common use for command lists is to run command sequentially. All commands entered on a line are ran one after the other (sequentially) if they are separated by a semicolon ( ; ). Meaning, the first command is ran, the shell waits for it to complete, then moves on to the next command in the list.

[savona@putor ~]$ whoami; cat /etc/redhat-release; uptime
 savona
 Fedora release 30 (Thirty)
 07:46:38 up 1 day, 23:02,  1 user,  load average: 0.52, 0.53, 0.37

When using the semicolon in lists, the next command executes regardless of the exit status of the command before it. If a command fails, the shell prints the error to standard error (STDERR) and the shell will move on to the next command in the list.

[savona@putor ~]$ whoami; cat /no/file/here; uptime
 savona
 cat: /no/file/here: No such file or directory
 07:52:43 up 1 day, 23:08,  1 user,  load average: 0.27, 0.38, 0.35

The return status code of a sequential list is the exit status of the last command executed. Just as if you were entering the commands one by one on the command line.

[savona@putor ~]$ whoami; cat /no/file/here; uptime
 savona
 cat: /no/file/here: No such file or directory
  07:55:41 up 1 day, 23:11,  1 user,  load average: 0.45, 0.46, 0.39

 [savona@putor ~]$ echo $?
 0

In the example above, the exit code is 0 (success) because the last command completed without errors. If we change the sequence of commands and make the last command fail, the exit code is 1 (error).

 [savona@putor ~]$ whoami; uptime; cat /no/file/here
 savona
  07:55:54 up 1 day, 23:11,  1 user,  load average: 0.35, 0.44, 0.38
 cat: /no/file/here: No such file or directory

 [savona@putor ~]$ echo $?
 1

Run Linux Commands Asynchronously

Contrary to the semicolon ';' the ampersand '&' allows the shell to run commands asynchronously, or in the background. A subshell executes commands separated by the '&' control operator. The parent shell does not wait for the command to finish and instantly returns you back to the prompt. This is why it is often referred to as running commands in the background.

[savona@putor ~]$ whoami & cat /etc/redhat-release & uptime &
 [1] 11397
 [2] 11398
 [3] 11399
 Fedora release 30 (Thirty)
 savona
  08:07:18 up 1 day, 23:23,  1 user,  load average: 0.24, 0.41, 0.43
 [1]   Done                    whoami
 [2]-  Done                    cat /etc/redhat-release
 [3]+  Done                    uptime

In the example above, you can see we ran three commands all terminated with the '&' operator. The shell returned the output out of order because the commands did not run sequentially. The cat command finished before the whoami command, which is why it's output is displayed first.

The numbered output you see in brackets is a function of job control. It shows the job number and the process ID of each job started and sent to the background. No exit code it printed if the command exits without an error (success). If the exit status is non-zero (failed) the return code will be printed.

Here is an example of a non-zero exit status.

[savona@putor ~]$ cat /path/to/no/file &
 [1] 11729
 cat: /path/to/no/file: No such file or directory
 [1]+  Exit 1                  cat /path/to/no/file

Using Logical AND and OR Control Operators

In Bash, AND (&&) and OR (||) lists are commands separated by operators that use the exit status of the prior command to determine if the shell should execute the next command.

The Bash Logical AND List

The AND '&&' operators tell the shell to run the next command in the list ONLY if the first command exits with a status of zero (success). This comes in handy when running multiple commands that depend on the success of the command before it.

For example, let's say you wanted to update a system then reboot to load a new kernel. You can use the AND operator to ensure the update is successful before rebooting like so:

sudo yum update -y && sudo reboot

The reboot command will ONLY execute if the yum command returns successful (exit status 0).

The Bash Logical OR List

Adversely, the OR '||' operator tells the shell to run the next command in the list ONLY if the preceding command exits with a non-zero status (failure).

Here is an example:

[savona@putor ~]$ cat /etc/redhat-release || whoami
 Fedora release 30 (Thirty)

In the example above, the first command executed successfully (exit status 0), which caused the second command to NOT run. Remember, when using the OR '||' operator the second command ONLY runs if the first command exits in a non-zero status (failure).

Below we replace the first command to ensure it fails. Thus, the shell will execute the second command in the list.

[savona@putor ~]$ cat /no/file || whoami
 cat: /no/file: No such file or directory
 savona

Conclusion

Learning the basics of Bash Command lists is important for any system administrator or DevOps engineer. In this article we covered the basic control operators that make up a Bash command list. We learned how to run Linux commands sequentially and asynchronously. We also learned how to use the logical AND and OR on the command line. As a Linux SysAdmin I use most of these lists in my day to day operations. If you have any questions or comments feel free to sound off in the comments section below.

Resources and Links