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.