Example of a Korn shell script
Let's look at a script that searches C source and header files in the current directory tree for a string passed on the command line.
#!/bin/sh
#
# tfind:
# script to look for strings in various files and dump to less
case $# in
1)
find . -name '*.[ch]' | xargs grep $1 | less
exit 0 # good status
esac
echo "Use tfind stuff_to_find "
echo " where : stuff_to_find = search string "
echo " "
echo "e.g., tfind console_state looks through all files in "
echo " the current directory and below and displays all "
echo " instances of console_state."
exit 1 # bad status
case $# in
1)
...
esac
The case ... in
is a shell builtin command, one of the
branching structures provided by the Korn shell, and is equivalent to the C
switch
statement.
The $#
is a shell variable. When you refer to a variable
in a shell, put a $
before its name to tell the shell that it's a
variable rather than a literal string. The shell variable, $#
, is a
special variable that represents the number of command-line arguments to the script.
The 1)
is a possible value for the case, the equivalent of the C
case
statement. This code checks to see if you've passed exactly
one parameter to the shell.
The esac
line completes and ends the case
statement.
Both the if
and case
commands use the command's name
reversed to represent the end of the branching structure.
find . -name '*.[ch]' | xargs grep $1 | less
find . -name '*.[ch]'
xargs grep $1
less
which are joined by the |
or pipe character. A pipe is one
of the most powerful things in the shell; it takes the output of the program on the
left, and makes it the input of the program to its right. The pipe lets you build
complex operations from simpler building blocks. For more information, see
Redirecting input and output
in the Using the Command Line chapter.
The first piece, find . -name '*.[ch]'
, uses another
powerful and commonly used command. Most filesystems are recursive through a hierarchy
of directories, and
find
is a utility that descends through the hierarchy of directories recursively. In
this case, it searches for files that end in either .c or
.h—that is, C source or header files—and prints
out their names.
The filename wildcards are wrapped in single quotes ('
)
because they're special characters to the shell. Without the quotes, the shell would
expand the wildcards in the current directory, but we want find to
evaluate them, so we prevent the shell from evaluating them by quoting them. For more
information, see
Quoting special characters
in Using the Command Line.
xargs grep $1
, does a couple of things:
- grep
is a file-contents search utility. It searches the files given on its
command line for the first argument. The
$1
is another special variable in the shell that represents the first argument we passed to the shell script (i.e., the string we're looking for). -
xargs is a utility that takes its input and turns it into command-line parameters for some other command that you give it. Here, it takes the list of files from find and makes them command-line arguments to grep. In this case, we're using xargs primarily for efficiency; we could do something similar with just find:
which loads and runs the grep program for every file found.find . -name '*.[ch]' -exec grep $i {} | less
The command that we actually used:
runs grep only when xargs has accumulated enough files to fill a command line, generally resulting in far fewer invocations of grep and a more efficient script.find . -name '*.[ch]' | xargs grep $1 | less
The final piece, less, is an output pager. The entire command may generate a lot of output that might scroll off the terminal, so less presents this to you a page at a time, with the ability to move backwards and forwards through the data.
case
statement also includes the following after the
find command:
exit 0 # good status
This returns a value of 0 from this script. In shell programming, zero means true or success, and anything nonzero means false or failure. (This is the opposite of the meanings in the C language.)
echo "Use tfind stuff_to_find "
echo " where : stuff_to_find = search string "
echo " "
echo "e.g., tfind console_state looks through all files in "
echo " the current directory and below and displays all "
echo " instances of console_state."
exit 1 # bad status
is just a bit of help; if you pass incorrect arguments to the script, it prints a
description of how to use it, and then returns a failure code.