< Day Day Up > |
12.6 Shell ScriptsA shell script is simply a file that contains a set of commands to be run by the shell when invoked. By storing commands as a shell script, you make it easy to execute them again and again. As an example, consider a file named deleter, which contains the following lines: echo -n Deleting the temporary files... rm -f *.tmp echo Done. The echo commands simply print text on the console. The -n option of the first echo command causes omission of the trailing newline character normally written by the echo command, so both echo commands write their text on a single line. The rm command removes all files having names ending in .tmp from the current working directory. You can execute this script by issuing the sh command, as follows: $ sh deleter
If the deleter file were in a directory other than the current working directory, you'd have to type an absolute path, for example: $ sh /home/bill/deleter You can make it a bit easier to execute the script by changing its access mode to include execute access. To do so, issue the following command: $ chmod 555 deleter This gives you, members of your group, and everyone else the ability to execute the file. To do so, simply type the absolute path of the file, for example: $ /home/bill/deleter If the file is in the current directory, you can issue the following command: $ ./deleter You may wonder why you can't simply issue the command: $ deleter In fact, this still simpler form of the command will work, so long as deleter resides in a directory on your search path. You'll learn about the search path later. Linux includes several standard scripts that are run at various times. Table 12-2 identifies these and gives the time when each is run. You can modify these scripts to operate differently. For example, if you want to establish command aliases that are available whenever you log in, you can use a text editor to add the appropriate lines to the .profile file that resides in your /home directory. Since the name of this file begins with a dot (.), the ls command won't normally show the file. You must specify the -a option in order to see this and other hidden files.
12.6.1 Input/Output Redirection and PipingThe shell provides three standard data streams:
By default, most programs read their input from stdin and write their output to stdout. Because both streams are normally associated with a console, programs behave as you generally want, reading input data from the console keyboard and writing output to the console screen. When a well-behaved program writes an error message, it writes the message to the stderr stream, which is also associated with the console by default. Having separate streams for output and error messages presents an important opportunity, as you'll see in a moment. Although the shell associates the three standard input/output streams with the console by default, you can specify input/output redirectors that, for example, associate an input or output stream with a file. Table 12-3 summarizes the most important input/output redirectors.
To see how redirection works, consider the wc command. This command takes a series of filenames as arguments and prints the total number of lines, words, and characters present in the specified files. For example, the command: $ wc /etc/passwd might produce the output: 22 26 790 /etc/passwd which indicates that the file /etc/passwd contains 22 lines, 26 words, and 790 characters. Generally, the output of the command appears on your console. But consider the following command, which includes an output redirector: $ wc /etc/passwd > total If you issue this command, you won't see any console output, because the output is redirected to the file total, which the command creates (or overwrites, if the file already exists). If you execute the following commands: $ wc /etc/passwd > total $ cat total you will see the output of the wc command on the console. Perhaps you can now see the reason for having the separate output streams stdout and stderr. If the shell provided a single output stream, error messages and output would be mingled. Therefore, if you redirected the output of a program to a file, any error messages would also be redirected to the file. This might make it difficult to notice an error that occurred during program execution. Instead, because the streams are separate, you can choose to redirect only stdout to a file. When you do so, error messages sent to stderr appear on the console in the usual way. Of course, if you prefer, you can redirect both stdout and stderr to the same file or redirect them to different files. As usual in the Unix world, you can have it your own way. A simple way of avoiding annoying output is to redirect it to the null device file, /dev/ null. If you redirect the stderr stream of a command to /dev/null, you won't see any error messages the command produces. For example, the grep command prints an error message if you invoke it on a directory. So, if you invoke the grep command on files in the current directory (*) and the current directory contains subdirectories, you'll see unhelpful error messages. To avoid them, use a command like this one, which searches for files containing the text "localhost": $ grep localhost * 2>/dev/null Just as you can direct the standard output or error stream of a command to a file, you can also redirect a command's standard input stream to a file so the command reads from the file instead of the console. For example, if you issue the wc command without arguments, the command reads its input from stdin. Type some words and then type the end-of-file character (Ctrl-D), and wc will report the number of lines, words, and characters you entered. You can tell wc to read from a file, rather than the console, by issuing a command like: $ wc </etc/passwd Of course, this isn't the usual way of invoking wc. The author of wc helpfully provided a command-line argument that lets you specify the file from which wc reads. However, by using a redirector, you could read from any desired file even if the author had been less helpful.
When you specify no command-line arguments, many Unix programs read their input from stdin and write their output to stdout. Such programs are called filters. Filters can be easily fitted together to perform a series of related operations. The tool for combining filters is the pipe, which connects the output of one program to the input of another. For example, consider this command: $ ls ~ | wc -l The command consists of two commands, joined by the pipe redirector (|). The first command lists the names of the nonhidden files in the user's home directory, one file per line. The second command invokes wc by using the -l option, which causes wc to print only the total number of lines, rather than printing the total number of lines, words, and characters. The pipe redirector sends the output of the ls command to the wc command, which counts and prints the number of lines in its input, which happens to be the number of files in the user's home directory. This is a simple example of the power and sophistication of the Unix shell. Unix doesn't include a command that counts the files in the user's home directory and doesn't need to do so. Should the need to count the files arise, a knowledgeable Unix user can prepare a simple script that computes the desired result by using general- purpose Unix commands. 12.6.2 Shell VariablesIf you've studied programming, you know that programming languages resemble algebra. Both programming languages and algebra let you refer to a value by a name. And both programming languages and algebra include elaborate mechanisms for manipulating named values. The shell is a programming language in its own right, letting you refer to variables known as shell or environment variables. To assign a value to a shell variable, you use a command that has the following form: $ variable = value For example, the command: $ DifficultyLevel=1 assigns the value 1 to the shell variable named DifficultyLevel. Unlike algebraic variable, shell variables can have nonnumeric values. For example, the command: $ Difficulty=medium assigns the value medium to the shell variable named Difficulty. Shell variables are widely used within Unix, because they provide a convenient way of transferring values from one command to another. Programs can obtain the value of a shell variable and use the value to modify their operation, in much the same way they use the value of command-line arguments. You can see a list of shell variables by issuing the set command. Usually, the command produces more than a single screen of output. So, you can use a pipe redirector and the less command to view the output one screen at a time: $ set | less Press the spacebar to see each successive page of output. You'll probably see several of the shell variables described in Table 12-4 among those printed by the set command. The values of these shell variables are generally set by one or another of the startup scripts described earlier in this chapter.
You can use the value of a shell variable in a command by preceding the name of the shell variable by a dollar sign ($). To avoid confusion with surrounding text, you can enclose the name of the shell variable within curly braces ({ }); it's good practice (though not necessary) to do so consistently. For example, you can change the current working directory to your /home directory by issuing the command: $ cd ${HOME} Of course, issuing the cd command with no argument causes the same result. However, suppose you want to change to the /work subdirectory of your home directory. The following command accomplishes exactly that: $ cd ${HOME}/work An easy way to see the value of a shell variable is to specify the variable as the argument of the echo command. For example, to see the value of the HOME shell variable, issue the command: $ echo ${HOME} To make the value of a shell variable available not just to the shell, but to programs invoked by using the shell, you must export the shell variable. To do so, use the export command, which has the form: $ export variable where variable specifies the name of the variable to be exported. A shorthand form of the command lets you assign a value to a shell variable and export the variable in a single command: $ export variable = value You can remove the value associated with a shell variable by giving the variable an empty value: $ variable = However, a shell variable with an empty value remains a shell variable and appears in the output of the set command. To dispense with a shell variable, you can issue the unset command: $ unset variable
Once you unset the value of a variable, the variable no longer appears in the output of the set command. 12.6.3 The Search PathThe special shell variable PATH holds a series of paths known collectively as the search path. Whenever you issue an external command, the shell searches the paths that comprise the search path, seeking the program file that corresponds to the command. The startup scripts establish the initial value of the PATH shell variable, but you can modify its value to include any desired series of paths. You must use a colon (:) to separate each path of the search path. For example, suppose that PATH has the following value: /usr/bin:/bin:/usr/local/bin:/usr/bin/X11:/usr/X11R6/bin You can add a new search directory, say /home/bill, by issuing the following command: $ PATH=${PATH}:/home/bill Now, the shell will look for external programs in /home/bill as well as in the default directories. However, the problem is that the shell will look there last. If you prefer to check /home/bill first, issue the following command instead: $ PATH=/home/bill:${PATH} The which command helps you work with the PATH shell variable. It checks the search path for the file specified as its argument and prints the name of the matching path, if any. For example, suppose you want to know where the program file for the wc command resides. Issuing the command: $ which wc will tell you that the program file is /usr/bin/wc (or whatever other path is correct for your system). 12.6.4 Quoted StringsSometimes the shell may misinterpret a command you've written, globbing a filename or expanding a reference to a shell variable that you hadn't intended. Of course, it's actually your interpretation that's mistaken, not the shell's. Therefore, it's up to you to rewrite your command so the shell's interpretation is congruent with what you intended. Quote characters, described in Table 12-5, can help you by controlling the operation of the shell. For example, by enclosing a command argument within single quotes, you can prevent the shell from globbing the argument or substituting the argument with the value of a shell variable.
To see quoted characters in action, consider how you might cause the echo command to produce the output $PATH. If you simply issue the command: $ echo $PATH the echo command prints the value of the PATH shell variable. However, by enclosing the argument within single quotes, you obtain the desired result: $ echo '$PATH' Double quotes have a similar effect. They prevent the shell from globbing a filename but permit the expansion of shell variables. Backquotes operate differently; they let you execute a command and use its output as an argument of another command. For example, the command: $ echo My home directory contains `ls ~ | wc -?` files. prints a message that gives the number of files in the user's /home directory. The command works by first executing the command contained within backquotes: $ ls ~ | wc -l This command, as explained earlier, computes and prints the number of files in the user's directory. Because the command is enclosed in backquotes, its output is not printed; instead the output replaces the original backquoted text. The resulting command becomes: echo My home directory contains 22 files. When executed, this command prints the output: My home directory contains 22 files. You may now begin to appreciate the power of the Linux shell: by including command aliases in your bashrc script, you can extend the command repertoire of the shell. And, by using filename completion and the history list, you can reduce the amount of typing it takes to enter frequently used commands. Once you grasp how to use it properly, the Linux shell is a powerful, fast, and easy-to-use interface that avoids the limitations and monotony of the more familiar point-and-click graphical interface. But the shell has additional features that extend its capabilities even further. As you'll see in the next section, the Linux shell includes a powerful programming language that provides argument processing, conditional logic, and loops. |
< Day Day Up > |