Shell Script Basics
You are familiar with a variety of Unix commands. You execute them by typing them at the command line, the pressing the enter key. There is another way to make such commands execute, and it's usually used for groups of commands. If you create an ordinary text file with an editor, and type various Unix commands in the file line by line, then you can execute them collectively in sequence, first line through last, by giving the file's name on the command line then pressing the enter key. This is called shell programming. The files are called shell scripts. Because, to go one step further, shell programs can also contain "language constructs" like conditional execution (if statement), repetitive execution (for or while statement), variables, and other programming language features, you can build useful programs this way.
The detailed description of shell programming is found in textbooks about Unix. Here, to illustrate the basics, are several small programs annotated to explain what they are doing. All of the shell programs discussed below can be found on the remote Unix machine in the /home/public/class/shell-programming subdirectory. Copy them into your home directory and experiment with them as you read the following. That is by far the best way to learn and remember it.
Basic Mechanism
The following program is contained in a file named "won.1". Notice it has 6 lines of text in it, but the first 5 don't count because they all begin with a # symbol. That makes these lines "comments" so that they are not executed at the time you run the program. Only the 6th line will be executed. It conatins a perfectly good Unix command, who, with its output piped to another Unix command, wc -l. Recall that the who command produces a list of users logged into the computer, and wc is the "word count" command. But adding -l to wc turns it into a "line count" command. By counting the lines produced by running the who command, you are counting the number of users logged in to the computer. The point is that "who | wc -l" is a perfectly good command line, much as you might normally type in at the command prompt and then hit the Enter key. But here it is sitting in a file, from where it can be executed.
[root@EMACH1
shellprogramming]# cat won.1 # # won # a simple program to display the # number of users currently logged on # who | wc -l |
And here's what happens when you execute it:
[root@EMACH1
shellprogramming]# ./won.1 3 |
Notice that at the command line you supply the name of the file, won.1. But what gets executed is the who and wc commands within. (And since the output is "3" there must be three users currently logged in.) This demonstrates the fundamental mechanism of shell programming. You might be familiar with the concept from other operating environments, where it is sometimes called "batch programming."
Before proceeding, note there are two mechanical requirements to enabling shell script execution. First, the script must be "executable." Second, the script must be "findable" for execution. Making the script (that is, the file) executable is a matter of Unix file permissions. After freshly editing a script file it probably will not be executable, but you can make it so by issuing the following command:
chmod +x <filename>
The file must also be "findable." You can make sure it will be found by giving its full name, including path prefix, on the command line. If it is in the current directory you accomplish the same thing by using the shorthand "./" (period, slash) as a prefix to the filename. In this example ./won.1 says, "Please execute a file whose name is won.1, and whose location is in the current directory."
Let's get elaborate, by having multiple commands this time:
#[root@EMACH1
shellprogramming]# cat won.2 # # won # a simple program to display the # number of users currently logged on # date who | wc -l pwd |
This illustrates that a shell script can contain an arbitrary sequence of commands-- whatever you want. If you can use it on the regular command line, you can put it into a script. And the results when won.2 is executed:
[root@EMACH1
shellprogramming]# ./won.2 Mon May 7 10:03:45 PDT 2001 <-- from "date" 3 <-- from "who | wc -l" /root/shellprogramming <-- from "pwd" |
Note that the output consists of the results of the 3 commands in the script, in order. Mon May 7... came from "date," 3 came from "who...," and /root/shellprogramming came from "pwd."
Talking to the User
Shell programming can be used for real, useful progamming. That usually means communicating with the user more meaningfully by printing information he can read on the screen. How could that be done in a script? Well, how could it be done with a command at the prompt? You know the answer. Here is a "prettier" version of the above program:
[root@EMACH1
shellprogramming]# cat won.3 # # won # a simple program to display the # number of users currently logged on # echo echo -e 'Date and Time: \c' date echo -e "Number of users on the system: \c" who | wc -l echo -e "Your current directory: \c" pwd echo |
It's the echo command that prints to the screen. The results are lovely:
[root@EMACH1
shellprogramming]# ./won.3 Date and Time: Mon May 7 10:03:56 PDT 2001 Number of users on the system: 3 Your current directory: /root/shellprogramming |
Another way to code this more compactly would combine into a single line the 2 lines giving the command itself and the prompt that goes with it:
[root@EMACH1
shellprogramming]# cat won.5 # # won # a simple program to display the # number of users currently logged on # echo echo -e 'Date and Time: \c'; date <-- 2 commands, semi-colon separated echo -e "Number of users on the system: \c"; who | wc -l echo -e "Your current directory: \c"; pwd echo |
Note the use of the semi-colon between commands on the same line. This is a standard command-line feature, so it translates to shell script (remember, anything you can use on the command line can happily be used in a script). When you enter a series of semi-colon-separated commands on the command line they are executed in sequence from left to right.
You can produce the same output with another technique called command substitution, very useful in shell scripts. Whenever you embed into a line a Unix command surrounded with single-back-quote characters, the command is executed and its output replaces the appearance of the command in the line.
[root@EMACH1
shellprogramming]# cat won.4 # # won # a simple program to display the # number of users currently logged on # echo echo -e "Date and Time: \c`date`\n" <-- `date` gets replaced with date's output echo -e "Number of users on the system: \c`who | wc -l`\n" echo -e "Your current directory: \c`pwd`\n" echo |
Note that the single-back-quote character is different from the regular single-quote character. It is in the extreme upper left on most keyboards. With either technique, compound commands with semi-colon or embedded output with single-back-quote, the output is the same:
Date and Time: Mon May
7 10:04:12 PDT 2001 Number of users on the system: 3 Your current directory: /root/shellprogramming |
Letting the User Talk to You
You can also let the user communicate with your script, using the command "read". It waits for keyboard input until the Enter key is pressed.
[root@EMACH1
shellprogramming]# cat kb_read.1 # # The read command example # echo -e "Enter your name: \c" <-- prompt the user read name <-- read from keyboard, capture input in variable "name" echo -e "Your name is $name." <-- echo back the captured data |
Notice that "read" has a target called "name". Name is a shell variable, much like variables in other programming languages. Often a language requires you to declare in advance the names of variables a program will use. Shell scripts don't have that requirement. The action of the read command is to absorb what you type at the keyboard and place it into the variable given. Also note the use of "$name" in the last line. The dollar sign in front of the variable's name has a special meaning. It causes the variable's contents to replace "$name" so the contents is what gets echoed. Here's the output:
[root@EMACH1
shellprogramming]# ./kb_read.1 Enter your name: George W. Bush Your name is George W. Bush. |
The action of the read command is to absorb what you type in at the keyboard, and place it into the variable given. If you give it two or more variables, it will put separate words of input into each variable and an leftover words into the last variable:
[root@EMACH1
shellprogramming]# cat kb_read.2 # # The read command example # echo -e "Give me a long sentence." read word1 word2 rest <-- read user response into 3 variables echo -e "$word1 \n $word2 \n $rest" |
The user's input will be parceled out 1 word at a time among the 3 variables word1, word2, and rest. In the echo command, \n says to issue a line feed and move down to the next line when printing the output. So word2 is supposed to show up 1 line below word1, and rest is supposed to be one line below that.
[root@EMACH1
shellprogramming]# ./kb_read.2 Give me a long sentence. The sky is blue and beautiful. The sky is blue and beautiful. |
word1 received "The," word2 received "sky," and rest received all the other 4 words. They were printed on 3 lines, one for each variable.
Special Shell Variables
There are a number of special shell variables that begin with a dollar sign.
$0 | contains the name of the script, as typed on the command line |
$1, $2, $3, ... $9 | contains the 1st through 9th command line parameters respectively |
$# | contains the number of command line parameters |
$@ | contains all command line parameters: "$1 $2 ... $9" |
$? | contains the exit status of the last command |
$* | contains all command line parameters "$1 $2 ... $9" |
$$ | contains the PID (process ID) number of the executing process |
Here is a program that employs several of these:
[root@EMACH1
shellprogramming]# cat BOX echo "The following is the output of $0 script." echo "Total number of command line arguments: $#" echo "The first parameter: $1" echo "The second parameter: $2" echo "This is the list of all the parameters: $*" |
and its output:
[root@EMACH1
shellprogramming]# ./BOX Today is a beautiful day. The following is the output of ./BOX script. Total number of command line arguments: 5 The first parameter: Today The second parameter: is This is the list of all the parameters: Today is a beautiful day. |
On the remote Unix server you can find these and similar examples. They are in directory /home/public/class/shellprogramming.