dcsimg

Learning a Script

It seems that one of the skills required of an actor is the ability to memorize a script. I had assumed that learning a script is of greater importance to stage actors than film actors, because stage actors must deliver their lines and follow their stage directions correctly the first time. However, acquaintances that are in the business assure me scripts play a central role in both types of acting.

It seems that one of the skills required of an actor is the ability to memorize a script. I had assumed that learning a script is of greater importance to stage actors than film actors, because stage actors must deliver their lines and follow their stage directions correctly the first time. However, acquaintances that are in the business assure me scripts play a central role in both types of acting.

Scripts of a somewhat different kind are important to Linux and Unix users. Scripts (sometimes referred to as “shell scripts,” since the scripting language is built into the shell) let you teach a computer new commands of your own design. By constructing scripts that perform commonly used operations, you can reduce the tedium and effort these operations require. A thorough knowledge of scripting is one of the keystones of Linux mastery.

A First Script

With all the copyright disputes that have arisen lately, I’m surprised nobody has commented on Burger King’s ownership of the slogan “Have It Your Way.” That’s been the slogan of Linux and Unix developers since the beginning; if you don’t like how your system works, write a script to make it work your way.

For instance, suppose you often use the w command to see which users are logged into a host (see Figure One).




Figure One: Output of the w Command


[bmccarty@home bmccarty]$ w
10:06am up 149 days, 22:01, 38 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
dglyer pts/0 faedhcp117 Thu10am 23:54m 0.73s 0.01s /usr/local/bin/pdme
sniper86 pts/25 – 10:05am 1.00s 0.07s 0.05s vi . te/aliases.irc
enigma pts/22 – 7:33am 35:18 60.41s 60.41s irc -c#tech enigma
philpi pts/31 – 9:13am 5:34 0.30s 0.30s pine
mccartyp pts/21 – 9:55am 11:02 48.52s 48.51s pine -i
enigma pts/1 – 7:33am 35:40 1.16s 1.13s rc -c#linux enigma

etc.

It’s difficult to see if a specific user is logged on, because the w command prints its output in no particular order. By writing a script, however, you can change this behavior. Using a text editor, create a file named users with the following contents:


w | sort

Now, execute the file by issuing the following command:


sh users

The output will now resemble Figure Two (pg. 20), in which lines appear in an order governed by the user name.




Figure Two: Sorted Output of the w Command


[bmccarty@home bmccarty]$ w
10:06am up 149 days, 22:01, 38 users, load average: 0.00, 0.00, 0.00
dglyer pts/0 faedhcp117 Thu10am 23:54m 0.73s 0.01s /usr/local/bin/pdme
enigma pts/22 – 7:33am 35:18 60.41s 60.41s irc -c#tech enigma
enigma pts/1 – 7:33am 35:40 1.16s 1.13s irc -c#linux enigma
mccartyp pts/21 – 9:55am 11:02 48.52s 48.51s pine -i
philpi pts/31 – 9:13am 5:34 0.30s 0.30s pine
sniper86 pts/25 – 10:05am 1.00s 0.07s 0.05s vi .te/aliases.irc
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT

Okay, the heading line appears last in the output, but we’ll fix that in a moment. First, let’s look at how the script works. Essentially, typing the command shusers is no different than typing the command contained in the file w | sort. This command executes the w subcommand and then “pipes,” or sends, the w command’s output to the sort subcommand, which alphabetizes the w command’s output.

You may reasonably object on the grounds that it’s almost as convenient to type w | sort as it is to type sh users. Fair enough; let’s fix that. Issue the following command:


chmod u+x users

Now you can obtain the sorted listing by merely typing the command ./users. That’s easier than typing w | sort, isn’t it? Moreover, if you put the users file in a directory that’s contained in your PATH environment variable, you can simply type users.

If this hasn’t convinced you of the value of scripts, consider that scripts can contain multiple commands. Let’s demonstrate by moving the pesky heading line from our last example to the top of the output, where it belongs. To do this, we simply modify our users script to have the contents shown in Figure Three.




Figure Three: Fixing the Heading


echo “USER    TTY     FROM           LOGIN@  IDLE  JCPU  PCPU  WHAT”
w -h | sort

If you execute this script, you’ll see output that looks just like the original output of the w command, but the output appears in sorted order. In particular, the heading appears at the top of the output. How is this done? The echo command displays the desired heading, and the -h flag of the w command suppresses the heading that command otherwise displays.

Why stop here? Let’s continue by creating a script that provides still more useful output.

Consider Figure Four. This supercharged descendant of the virile, yet undistinguished, w command displays the total number of current users as the final line of its output. This is accomplished by using the echo command to display an appropriate label and then using the w command a second time, piping its output to the wc command, which counts and displays the number of lines in its input. Since the wc command receives one line of input for each user, the result is a count of logged-in users. As you can see, Linux is truly a “Have It Your Way” operating system.




Figure Four: Counting the Total Users


echo “USER    TTY     FROM           LOGIN@  IDLE  JCPU  PCPU  WHAT”
w -h | sort
echo -n “Total current users: ”
w -h | wc -l

Script Arguments

In the entertainment business, script arguments are bad news, because they can result in the loss of millions of dollars. However, in the Linux world, script arguments are good news, because they increase the flexibility of scripts.

In the context of Linux, the word argument doesn’t refer to a dispute. Instead, it refers to a value that modifies the operation of a script, allowing you to create more flexible scripts. Suppose you are interested in running the w command in order to see if your friend, whose userid is sniper86, is logged on right now. The script in Figure Five would do the job.




Figure Five: Finding a Specific User


echo “USER    TTY     FROM           LOGIN@  IDLE  JCPU  PCPU  WHAT”
w | grep sniper86

As before, the w command lists the logged-on users. Here, the grep command filters the resulting output, discarding all lines that don’t contain the text sniper86.

If you placed this script in the file friend, you could conveniently check to see if sniper86 is logged on. But suppose you have two friends. You could create a second file, perhaps named friend2, to check whether your other friend was logged on. However, if you are sociable and have many friends, this approach quickly becomes tedious. Fortunately, there’s a better way.

Consider Figure Six (pg. 20). Instead of containing the userid of one of your friends, this script contains the shell variable $1, which stands for the first argument provided by the user when the script is run. To see what this means, execute the script as follows:




Figure Six: Finding Any User


echo “USER    TTY     FROM           LOGIN@  IDLE  JCPU  PCPU  WHAT”
w | grep $1


friend sniper86

The command argument sniper86 is bound to the shell variable $1 so that the script behaves as though its second line was w | grepsniper86 rather than w|grep $1.

What’s cool about using an argument is that you can invoke the script in a nearly infinite variety of ways, accommodating the possibility of a practically infinite number of friends. For example, you can check whether enigma is logged on by issuing the command friendenigma. No longer is a special version of the script required for each of your friends.

To Quote or Not to Quote

When arguments can consist of multiple words, script authoring can become a tad tricky. For example, consider the data file shown in Figure Seven. This file, named contacts, contains e-mail addresses and names of Linux Magazine contacts.




Figure Seven: The Contacts File

To help you search the file, you might choose to create a script named findcontact, resembling the following:


grep $1 contacts

Better yet, you might include the -i flag so that grep ignores case distinctions. Also, you might use an absolute path to specify the location of the contacts file so that using the findcontacts command doesn’t require you to change the current working directory to the one containing the contacts file:


grep -i $1 /root/contacts

To search for a contact, you might issue a command such as this:


findcontact bill

The command displays the line, or lines, in the contacts file that contain the text bill, without regard to case. In this example, it would display the line showing the e-mail address of Bill McCarty.




Figure Eight: The Revised Contacts File

That’s well and good, but suppose that someone with the name of Bill McMillan joins the Linux Magazine team, as shown in Figure Eight. Now the findcontact bill command yields two lines of output rather than a single line. Again, that’s well enough. However, suppose you’re interested only in the contact information for the newcomer, and so you issue the following command:


findcontact Bill McMillan

To our disappointment, the command will display two lines of output — the line for Bill McCarty and the line for Bill McMillan. The reason for this is that you’ve actually sent two arguments to the findcontacts command — Bill and McMillan. However, the script will only accept the first argument ($1). That is not exactly having it our way, is it?

To overcome the problem, you might attempt to enclose the arguments in quotes, converting them to a single argument consisting of two words:


findcontact “Bill McMillan”

Unfortunately, this fix doesn’t go quite far enough. When the shell variable $1 in the line


grep -i $1 /root/contacts

is replaced by “Bill McMillan”, the script operates as though the line had been written as


grep -i Bill McCarty /root/contacts

because the shell drops the surrounding quotes when it substitutes the value of the $1 variable for its name. The result is that the grep searches the files McCarty and /root/contacts for the text Bill. Of course, the file McCarty probably does not exist, so the output consists of both lines from /root/contacts containing the text Bill. This is not what’s wanted.

Fortunately, the fix is simple; both the command-line argument and the script itself must be properly quoted. Write the script like this:


grep “$1″ /root/contacts

And, here’s how to execute it:


findcontact “Bill McMillan”
nobody@linux-mag.com Bill McMillan

Although it required some ingenuity to get here, the script now behaves as desired. Linux’s reputation as a have-it-your-way operating system is secure.

The Long and Winding Road

This column, as usual, has scarcely touched the surface of its topic, shell scripts. Among the most useful shell features not covered are multiple arguments, more sophisticated rules and methods of quoting arguments, environment variables, conditional execution, looping, backquoted expressions, and signals. Future Newbies columns will touch on these stepping-stones to Linux mastery.

The topical development of this column was inspired by Chapter Three of my favorite Unix book, Brian W. Kernighan’s and Rob Pike’s The Unix Programming Environment (Prentice-Hall, 1984). If you’re rushing down the road to Linux gurudom, you need to get and read this book. It’s a terse, and therefore somewhat tough, read that demands concentrated attention, but your efforts will be rewarded many times over. Until next month, happy scripting!




Finding a Good Script


As everyone knows, the real-life actors we mentioned at the beginning of this article often have trouble finding a good script. In fact, some actors have been known to resort to writing scripts especially designed to showcase their talents. Okay, so maybe we’re stretching the actor analogy a bit thin here, but the point is that you too can write your own scripts. However, to be able to write effective scripts, you’ll need to employ a wide-ranging repertoire of Linux commands. The table below summarizes some Linux commands that are often used in scripts. Consult the man pages or other documentation to learn more about these commands.


Important Commands Often Used in Scripts

atExecute a command at a specified time

cat

Display contents of a file

chmod

Change file permissions

comm

Compare files

cp

Copy files

df

Display disk space usage of filesystems

diff

Compare text files

du

Displays disk space usage of directories

echo

Display text

eval

Evaluate an expression

grep

Find strings

kill

Terminate a process

lpr

Print data

less

Page through a file

ls

List file information

mail

Send a mail message

mkdir

Create a directory

mv

Move a file or directory

nice

Set process priority

nohup

Run a command immune to hang-ups

od

Display file contents in octal and other formats

passwd

Set a user’s password

pr

Format data for printing

ps

Display process information

rm

Remove a file

rmdir

Remove a directory

sh

Launch a shell

sleep

Pause a process

sort

Sort data

spell

Check spelling

tail

Display last lines of file

time

Time the execution of a script

times

Displays CPU time used

uniq

Eliminates duplicate data

w

Displays logged in users

wait

Waits for child processes to terminate

wc

Counts characters, words, and lines

who

Displays logged in users



Bill McCarty is an associate professor at Azusa Pacific University. He can be reached at bmccarty@apu.edu.

Comments are closed.