Specifying command execution order in Linux

Looking for a way to make command execution more efficient (and safe)? Jim McIntyre introduces you to command execution order and operators that will make your Linux/UNIX admin job amazingly easy.

System administration often requires a series of commands to be executed. For example, to remove a user from the system, move all text files in the user's home directory to a backup directory and then delete the user's home directory. I could accomplish this task by running the required commands one at a time, as shown here:
mv /home/jack/*txt /backup/users/jack
/usr/sbin/userdel -r jack

The problem with executing commands in this manner is that there are no conditions placed on the results of the first command. Whether or not I successfully move all of Jack's data files to a backup location, I am still able to delete the user Jack by using the -r option with the userdel command. This command will remove all files in Jack's home directory, Jack's mail spool, and all files related to Jack in /etc/passwd, /etc/group, and /etc/shadow.

This ability to run commands unconditionally makes it easy to make mistakes on critical areas of the system. However, Linux allows the use of logical operators and command grouping to execute series of commands. When logical operators are used, the results of one command determine whether or not the next command will be run. Command grouping allows administrators to run groups of several commands at once. This Daily Feature will provide an introduction to logical operators and command grouping in Linux.

Using the && operator
The && operator uses the following syntax:
command-1  &&       command-2

The && operator specifies that if command-1 executes successfully, then command-2 will be executed. In plain English, this command says, "If command-1 executes successfully, then run command-2."

If command-1 does not execute successfully, command-2 will not be executed. Let's try a simple example. From your Linux console, enter this command.

When this series of commands is run, you should see a listing of the files and directories in the current directory, along with the message, “This is a file listing for the current directory."

Now let's see what happens if the first command doesn't execute correctly. For the next example, we’ll add a command that we know won't run to test how the && operator works. From your console, enter this command.

Because the -zz option is an invalid option for the ls command, you should get the following result:
ls: invalid option – z
Try `ls --help' for more information.
[jim@admin sbin]#

Another example is shown below. In this case, the administrator wants to find all the files in /etc/sysconfig that have been modified in the last two days. Once the files have been found, the list of files will be written to a file in the administrator's home directory. If these two actions are completed successfully, a notice will be printed to the console. This is the command series to accomplish this. In this example, the following actions occur:
  1. 1.      The find command searches the /etc/sysconfig directory for any file modified in the last two days.
  2. 2.      The list of files matching this condition is redirected to the modified.txt file in the root user's home directory (> /root/modified.txt).
  3. 3.      If these two actions are successfully completed, the message "File search complete" is printed to the console.

Using the || operator
There will be instances when the administrator will want to specify which actions should be performed if a command does not execute successfully. This is where the logical || operator is used. The || operator uses the following syntax:
command-1 || command-2

The statement above is read, "If command-1 does not execute successfully, execute command-2."

Here is a simple example. In this example, the following two actions occur:
  1. 1.      All files in the user Jim’s home directory with the extension .doc are copied to the /backup/users/jim directory.
  2. 2.      If no files with the .doc extension are found, the message "No MS Word files found" is printed to the screen.

Grouping commands
The simplest way to group commands in Linux is to use the semicolon (;) as a command separator. For instance, when I compile software, the most common commands are:
make install

If I know the process of building the program will take a while, I can use the semicolon (;) command separator and enter all the commands at the same time. For instance, to install Apache from source code, I would use the following series:
./configure; make; make install

Now all three commands will run without further intervention from the console, assuming there are no errors when the program is built.

Using the () and {} operators
When commands are grouped, they are run either in the current shell or in a subshell. To run a series of commands in the current shell, commands are grouped using round brackets ().

The format for using the () operator is:
(command-1; command-2)

To execute a command in a subshell, use curly braces {}. The format for the {} operator is:
{command-1; command-2}

These operators are almost never used alone. They are best used to extend the capabilities of the && and || operators. I find these operators are most useful when I want to perform more than one action when a command fails.

To understand how these operators work, look at this example. Here, the following actions are specified:
  1. 1.      I want to use the find command to search for files in the /etc/cron.d directory, which may have been altered over the weekend.
  2. 2.      When the command fails, I want to generate the message, "No modified files found."
  3. 3.      The message will then be mailed to me (root).
  4. 4.      Once these actions are complete, I want the script to terminate through the exit command.

The problem here is that once the first command fails, the next command executed will be the last command, exit. This will terminate the script, and I will not know the results of the search.

To ensure that all commands following the || operator are executed, enclose all commands after the || operator with round brackets (). This example would ensure that I got a mail message following the failure of the find command. The () operator is very useful when the administrator knows a command will fail and other actions will be required.

Using logical operators and command grouping is a simple way to add a decision-making capability to basic system administration. Use your imagination to come up with nearly unlimited possibilities to make your Linux administration job both simple and fun!


Editor's Picks