Bash Shell Scripting/Simple Commands

A simple command consists of a sequence of words separated by spaces or tabs. The first word is taken to be the name of a command, and the remaining words are passed as arguments to the command. We have already seen a number of examples of simple commands; here are some more:


 * This command uses  ("change directory"; a built-in command for navigating through the filesystem) to navigate "up" one directory.
 * The notation  means "parent directory". For example,   is equivalent to.
 * Assuming the program  ("remove") is installed, this command deletes the files ,  , and   in the current directory.
 * Bash finds the program  by searching through a configurable list of directories for a file named   that is executable (as determined by its file-permissions).
 * This command runs the program located at, passing   as the sole argument.
 * must be executable (as determined by its file-permissions).
 * Warning: Please ensure that there is NO SPACE between the forward slash and any files following it. e.g. assuming the "foo" folder exists in the "root" directory, then executing the following command: "rm -r / foo" will destroy your computer if performed with "sudo" access. You have been warned. If you don't understand the preceding, don't worry about it for the moment.
 * If  is a text-file rather than a binary program, and its first line begins with , then the remainder of that line determines the interpreter to use to run the file. For example, if the first line of   is  , then the above command is equivalent to.
 * must be executable (as determined by its file-permissions).
 * Warning: Please ensure that there is NO SPACE between the forward slash and any files following it. e.g. assuming the "foo" folder exists in the "root" directory, then executing the following command: "rm -r / foo" will destroy your computer if performed with "sudo" access. You have been warned. If you don't understand the preceding, don't worry about it for the moment.
 * If  is a text-file rather than a binary program, and its first line begins with , then the remainder of that line determines the interpreter to use to run the file. For example, if the first line of   is  , then the above command is equivalent to.

That example with  bears special note, since it illustrates how you can create a Bash-script that can be run like an ordinary program: just include   as the first line of the script (assuming that that is where Bash is located on your system; if not, adjust as needed), and make sure the script has the right file-permissions to be readable and executable. For the remainder of this book, all examples of complete shell scripts will begin with the line.

Navigation
As we saw in the first example, the utiliy command  serves to navigate with given arguments. In Unix and Unix-like systems like GNU/Linux there are 2 types of paths that are named as relative and absolute file path. Relative paths are relative to your contemporary position whereas absolute paths are specific. Here is an example of navigating to an absolute path:

There are specific arguments for relative path navigation that make different kinds of shortcuts possible. Here are some of those arguments:

To navigate up 2 directories you need to type:

To navigate to the path you were before navigating to your current location you need to type:

To print the path that you are currently in, its possible to use another built-in Unix utility named.

Quoting
We saw above that the command  removes three separate files: ,  , and. This happens because Bash splits the command into four separate words based on whitespace, and three of those words become arguments to the program. But what if we need to delete a file whose name contains a space?

Bash offers several quoting mechanisms that are useful for this case; the most commonly used are single-quotes  and double-quotes. Either of these commands will delete a file named :

Within the quotation marks, the space character loses its special meaning as a word-separator. Normally we wrap an entire word in quotation marks, as above, but in fact, only the space itself actually needs to be enclosed;  or   is equivalent to.

Another commonly-used quoting mechanism is the backslash, but it works slightly differently; it quotes (or "escapes") just one character. This command, therefore, is equivalent to the above:

In all of these cases, the quoting characters themselves are not passed in to the program. (This is called quote removal.) As a result,  has no way of knowing whether it was invoked — for example — as   or as.

Filename expansion and tilde expansion
Bash supports a number of special notations, known as expansions, for passing commonly-used types of arguments to programs.

One of these is filename expansion, where a pattern such as  is replaced with the names of all files matching that pattern. For example, if the current directory contains the files,  ,  , and  , then this command:

is equivalent to this command:

Here the asterisk  means "zero or more characters"; there are a few other special pattern characters (such as the question mark , which means "exactly one character"), and some other pattern-matching rules, but this use of   is by far the most common use of patterns.

Filename expansion is not necessarily limited to files in the current directory. For example, if we want to list all files matching  inside the directory , we can write this:

which may expand to something like this:

If no files match a specified pattern, then no substitution happens; for example, this command:

will most likely just print.

What happens if we have an actual file named  that we want to refer to? (Yes, filenames can contain asterisks!) Then we can use either of the quoting styles we saw above, or use backslash to remove special meaning from asterisk. Either of these:

will print the actual file, rather than printing all files whose names end in.

Another, similar expansion is tilde expansion. Tilde expansion has a lot of features, but the main one is this: in a word that consists solely of a tilde, or in a word that begins with   (tilde-slash), the tilde is replaced with the full path to the current user's home directory. For example, this command:

will print out the names of all files named  in the current user's home directory.

Brace expansion
Similar to filename expansion is brace expansion, which is a compact way of representing multiple similar arguments. The following four commands are equivalent:

The first command lists each argument explicitly. The other three commands all use brace expansion to express the arguments more tersely: in the second command, all the possibilities  through   are given, separated by commas; in the third command, a numeric sequence is given ("from 1 to 5, incrementing by 1"); and the fourth command is the same as the third, but leaves the   implicit.

We can also list the files in the opposite order:

with the default increment size being  when the endpoint of the sequence is less than the starting-point.

Since in Bash, the first word of a command is the program that is run, we could also write the command this way:

but obviously that is not conducive to readability. (The same sort of thing, incidentally, can be done with filename expansion.)

Brace expansion, like filename expansion, can be disabled by any of the quoting mechanisms;,  , or   produces an actual literal curly-brace.

Redirecting output
Bash allows a command's standard output (file-descriptor 1) to be sent to a file, rather than to the console. For example, the common utility program  writes out a file to standard output; if we redirect its standard output to a file, then we have the effect of copying the contents of one file into another file.

If we want to overwrite the destination file with the command's output, we use this notation:

If we want to keep the existing contents of the destination file as they are, and merely append the command's output to the end, we use this notation:

However, not everything that a program writes to the console goes through standard output. Many programs use standard error (file-descriptor 2) for error-messages and some types of "logging" or "side-channel" messages. If we wish for standard error to be combined with standard output, we can use either of these notations:

If we wish for standard error to be appended to a different file from standard output, we use this notation:

In fact, we can redirect only standard error, while leaving standard output alone:

In all of the above examples, we can replace  with   if we want to overwrite the redirect-target rather than append to it.

Later we will see some more advanced things that we can do with output redirection.

Redirecting input
Just as Bash allows a program's output to be sent into a file, it also allows a program's input to be taken from a file. For example, the common Unix utility  copies its input to its output, such that this command:

will write out the contents of  to the console.

As we have already seen, this trick is not needed in this case, since  can instead be instructed to copy a specified file to its output; the above command is simply equivalent to this one:

This is the rule rather than the exception; most common Unix utilities that can take input from the console also have the built-in functionality to take their input from a file instead. In fact, many, including, can take input from multiple files, making them even more flexible than the above. The following command prints out  followed by  :

Nonetheless, input redirection has its uses, some of which we will see later on.

A preview of pipelines
Although they are outside the scope of this chapter, we now have the background needed for a first look at pipelines. A pipeline is a series of commands separated by the pipe character. Each command is run at the same time, and the output of each command is used as the input to the next command.

For example, consider this pipeline:

We have already seen the  utility;   simply writes the file   to its standard output. The program  is a common Unix utility that filters ("greps", in Unix parlance) based on a pattern; for example, the command   will print to its standard output any lines of input that contain the string. The command  uses the   option to invert the pattern; the command prints any lines of input that don't contain the string. Since the input to each command is the output of the previous command, the net result is that the pipeline prints any lines of  that do contain   and do not contain.