Bash provides a powerful tool for managing commands — conditional lists, which help build logical sequences for task execution. These lists allow you to control how and when commands are executed based on their results. In this article, we'll explore how to use the && (AND) and || (OR) operators, as well as unconditional lists, to effectively manage command sequences.
Conditional Lists
Suppose you need to create a file named report.log in the logs directory. A typical sequence of commands might look like this:
$ cd logs
$ touch report.log
Here, the execution of the second command depends on the successful change to the directory. If the logs directory does not exist, the touch command will throw an error. In Bash, you can explicitly define the dependency between these commands by combining them with the && (AND) operator:
$ cd logs && touch report.log
The command touch report.log will only execute if the change to the logs directory is successful. If the directory does not exist, the command will not run, avoiding an error.
This approach is commonly used in scripts where subsequent commands depend on the success of previous ones. For instance, if you are working with configuration files, you might back them up before making changes:
$ cp config.yaml config.yaml.bak && vim config.yaml
Here, the command vim config.yaml will only run if the backup creation is successful.
Checking Command Status
The && and || operators not only help manage command sequences but also allow you to check their status using the $? variable, which stores the return code of the last executed command. For example:
$ mkdir backups && cp data.db backups/
$ echo $?
0
If the backups directory is successfully created and the data.db file is copied, the return code will be 0, indicating successful execution. If any command fails, $? will return a non-zero value, enabling error handling in scripts.
The && and || Operators
The && operator executes the second command only if the first one succeeds. In contrast, the || operator runs the second command if the first one fails. Consider an example where you try to delete temporary files and, if unsuccessful, display a message:
$ rm temp/* || echo "Failed to remove temporary files"
Here, if the rm command cannot delete the files, an error message will be displayed.
These operators are often used for error management in scripts. For instance, to stop a script when directory creation fails:
$ mkdir logs || exit 1
In this case, the script execution will halt if the logs directory cannot be created.
Complex Sequences
The && and || operators can be combined to create more complex logical sequences. For example:
$ mkdir projects || echo "Directory exists" && cd projects || echo "Failed to enter directory"
Here, the shell attempts to create the projects directory. If it already exists, a message is displayed, followed by an attempt to enter the directory. If that fails, an error message is shown.
Unconditional Lists
In Bash, unconditional lists execute commands sequentially, regardless of the outcomes of previous commands. These lists are separated by a semicolon (;), for example:
$ sleep 300; tar -czf backup.tar.gz /home/user/documents
In this example, the tar command will execute five minutes after the sleep command completes. Importantly, neither command depends on the success of the other. Unconditional lists are useful for tasks where the result of previous operations is not critical, such as background jobs or independent tasks.
Difference Between ; and &&
While the semicolon (;) executes commands in order, it does not check the success of each one. In contrast, && ensures that the next command only runs if the previous one succeeds. From an efficiency perspective, using && avoids executing commands that would be pointless if prior commands fail.
Example of an Unconditional List
Here's an example of an unconditional list that executes multiple commands, regardless of potential errors:
mv file_a file_b; mv file_b file_c; mv file_c file_d
In this example, the commands execute sequentially, and only the return code of the last command (mv file_c file_d) is stored in the $? variable:
$ echo $?
0
If the last command fails, the return code will be non-zero, but the previous commands will have already been executed.
Conclusion
Combining conditional and unconditional lists in Bash provides a powerful tool for creating flexible and robust scripts. The && and || operators allow precise control over command sequences, ensuring they execute based on the success of previous commands. Unconditional lists are convenient for operations that do not depend on prior results.
Using these tools will help you build more reliable and efficient scripts, automate tasks, and minimize errors. Experiment with the examples provided to gain a deeper understanding of the possibilities they offer.