C Shell Programming


Although shell programming techniques apply to all shells , generally speaking, there are some differences exist between the C shell and the KornShell. If you are using the C shell, I want you to get off to a quick start, so I'll cover the basics of C shell programming in this section. I'll cover each shell programming technique briefly and use basic examples to help reinforce each technique. In all of the following shell programs, any line beginning with a " # " is a comment. This is true except for the very first line of a shell program, in which the shell the script is written for is executed. In all of the following programs, the C shell is executed with #!/bin/csh , which is the path of the C shell on the Solaris system used in the examples.

Command Substitution

The shell variables earlier covered can be used to save the output from a command. You can then use these variables when issuing other commands. The following shell program executes the date command and saves the results in the variable d . The variable d is then used within the echo command in the program cdate :

 #!/bin/csh # program "today" that provides the date set d='date +%x' echo "Today's date is $d" 

When we run cdate , the following is produced:

 martyp $  cdate  Today's date is 06/01/00 martyp $ 

The "+%x" in the above example produces the current date. Command substitution of this type is used in several upcoming shell scripts.

Reading User Input

Two common methods help you read user input to shell programs. The first is to prompt the user for information, and the second is to provide arguments to shell programs.

To begin, I'll cover prompting a user for information. A character, word, or sentence can be read into a variable. The following example first shows prompting the user for a word, and then a sentence :

 #!/bin/csh echo "Please enter your name:" set name = $< echo "hello, $name" echo "Please enter your favorite quote:" set quote = $< echo "Your favorite quote is:" echo $quote 

Here is an example of running this program:

 martyp $ userinput Please enter your name:  Marty  hello, Marty Please enter your favorite quote:  Creating is the essence of life.  Your favorite quote is: Creating is the essence of life. martyp $ 

Using this technique, you can prompt a user for information in a shell program. This technique is used in an upcoming program.

You can also enter command-line arguments. When you type the name of the shell script, you can supply arguments that are saved in the variables $1 through $9 . The first ten words on the command line are directly accessible in the shell program using the special variables $0 - $9 . This is how they work:

$0

The command name

$1

The first argument

$2

The second argument

$3

.

 

.

 

.

$9

The ninth argument

If you are not sure how many command-line arguments you may get when your program is run, there are two other variables that can help:

$#

The number of command-line arguments

$*

A space-separated list of all the command-line arguments (which does not include the command name)

The variable $* is commonly used with the for loop (soon to be explained) to process shell script command lines with any number of arguments.

The following script changes to the specified directory ( $1 ) and searches for the specified pattern ( $2 ) in the specified file ( $3 ):

 #!/bin/csh # search # Usage: search directory pattern file echo " " cd              # change to search dir and grep -n ""    # search for  in  echo " "          # print line endif 
graphics/grepa_icon.gif

grep is used to search a file for a pattern and print the line in which the pattern was found. awk , which was earlier covered, can be used to pick out a specific field within a line.

Here is an example of the search program:

 graphics/awka_icon.gif martyp $  search /home/martyp/shellprogs awk ifstat  12:# as one command so it can be easily piped to awk. 18:awk 'BEGIN { printf "%10s%10s%10s%10s%10s\n", "ipkts", 38:' # End of the awk program. martyp $ 

In this example, we run search in the directory /home/martyp/shellprogs , looking for the pattern awk in the file ifstat . The result of this search produces three lines in the file ifstat , in which awk appears. These are lines number 12, 18, and 38.

In the next section, we'll expand this program somewhat to include testing and branching.

Testing and Branching

There are many kinds of decision-making that your shell programs can perform. if provides the flexibility to make decisions and take the appropriate action. Let's expand the search script to verify that three arguments have been provided:

 #!/bin/csh # search # Usage: search directory pattern files if ($#argv != 3) then            # if < 3 args provided         echo "Usage: search directory pattern files"                                 # then print Usage else         echo " "                # else print line and         cd                    # change to search dir         grep -n ""          # search for  in          echo " "                # print line endif 

This program is called search1 . We run this program using the same arguments as we did with the search program; however, search1 is enhanced to provide a usage message if we don't provide arguments when we run it. The following example shows running search1 :

 martyp $  search1  Usage: search directory pattern files martyp $  search1 /home/martyp/shellprogs awk llsum  12:# drwxrwxrwx 2 gerry  aec  24  Mar 21 18:25   awk_ex 15:# awk field numbers: 18:awk ' BEGIN { x=i=0; printf "%-16s%-10s%8s%8s\n",\ martyp $ 
graphics/awka_icon.gif

On the first attempt to run search1 , we provided no arguments. The program checked to see whether we provided fewer than three arguments and produced the Usage message, because it found fewer than three arguments. Upon seeing the Usage message, it became clear how to use the program and we provided the required three arguments on the next attempt to run the program. In this example, we run search1 in the directory /home/martyp/shellprogs , looking for the pattern awk in the file llsum . The result of this search produces three lines in the file llsum , in which awk appears. These are lines number 12, 15, and 18.

Here are four commonly used forms of if :

1)

if (expression) command

2)


if  (expression)  then
                                           command(s)
endif

3)


if  (expression)  then
                                         command(s)
                     else
                                         command(s)
endif

4)


if  (expression)  then
                                         command(s)
                 [else  if  expression)  then
                                         command(s)]
                         .
                         .
                         .
                 [else
                                         command(s)]
endif

There are many operators that can be used in the C shell to compare integer values, such as the < used in the previous example. Here is a list of operators:

>

greater than

<

less than

>=

greater than or equal to

<=

less than or equal to

==

equal to

!=

not equal to

Looping

The C shell supports a number of techniques to support looping, including:

  1. The foreach loop, which takes a list of items and performs the commands in the loop once for each item in the list.

  2. The while loop, which executes a command (such as the test command) if the command executes successfully.

The format of the foreach loop is


foreach  name  (list)
                   command(s)
end

The following example uses a foreach loop to test whether or not the systems in the /etc/ hosts file are connected to the local host.

 #!/bin/csh #Program name: csh_hostck #This program will test connectivity to all other hosts in #your network listed in your /etc/hosts file. # It uses the awk command to get the names from the hosts file #and the ping command to check connectivity. #Note that we use /bin/echo because csh echo doesn't support #escape chars like \t or \c which are used in the #foreach loop. #Any line in /etc/hosts that starts with a number represents #a host entry. Anything else is a comment or a blank line. #Find all lines in /etc/hosts that start with a number and #print the second field (the hostname). set hosts='awk '/^[1-9]/ { print  }' /etc/hosts'                  # grave on outside, single quote on inside      /bin/echo "Remote host connection status:" foreach sys ($hosts)      /bin/echo "$sys - \c"                  # send one 64 byte packet and look for                  # the"is alive" message in                  # the output that indicates success.                  # messages vary between UNIX variants.      ping $sys 64 1  grep "is alive" > /dev/null      if ( $status == 0 ) then                echo "OK"      else                echo "DID NOT RESPOND"      endif end 
graphics/awka_icon.gif

The crazy-looking line with awk is used to obtain the name of remote hosts from the /etc/hosts file. The foreach loop takes all of the items in the list, the hosts in this case, and checks the status of each.

The hosts file on this system has three entries: the localhost, the LAN interface, and a DNS system. When we run the program in the following example, we expect to see a result for the testing of all three entries:

 martyp $  csh_hostck  Remote host connection status: localhost - OK sunsys - OK dnssrv1 - OK martyp $ 

All three entries in the hosts file have been evaluated with ping and produce a status of OK . When hardcoding information into scripts, such as the path of ping and the result you get from the ping command, please keep in mind that these may vary among different UNIX variants. One of the reasons you want to liberally comment your shell programs is to make them easy to modify under such circumstances.

You could use the while loop to execute commands for some number of iterations. The while loop is in the following format:


while  (expression)
                     command(s)
end

graphics/netstart_icon.gif

The following program, called netcheck , runs netstat at the desired interval, and prints out the heading once and the status of le0 nine times:

 #!/bin/csh # program to run netstat at every specified interval # Usage: netcheck interval set limit=9               # set limit on number times                           # to run netstat echo " " netstat -i  grep Name    # print netstat line with headings set count=0 while ($count<$limit)     # if limit hasn't reached                           # limit run netstat           netstat -i  grep le0           sleep         # sleep for interval                           # specified on command line           @ count++       # increment limit end echo "count has reached $limit, run netcheck again to see le0 status" 

Here is an example run of the netcheck program:

 martyp $  netcheck 3  Name  Mtu  Net/Dest     Address       Ipkts     Ierrs Opkts    Oerrs Collis Queue le0   1500 sunsys        sunsys         314374989  0     17252200 52135  7580906 le0   1500 sunsys        sunsys         314375038  0     17252203 52135  7580906 le0   1500 sunsys        sunsys         314375114  0     17252206 52135  7580906 le0   1500 sunsys        sunsys         314375185  0     17252209 52135  7580906 le0   1500 sunsys        sunsys         314375257  0     17252212 52135  7580906 le0   1500 sunsys        sunsys         314375332  0     17252215 52135  7580906 le0   1500 sunsys        sunsys         314375444  0     17252218 52135  7580906 le0   1500 sunsys        sunsys         314375508  0     17252221 52135  7580906 le0   1500 sunsys        sunsys         314375588  0     17252224 52135  7580906 count has reached 9, run netcheck again to see le0  status martyp $ 
graphics/netstart_icon.gif

The output of netcheck produces nine netstat outputs at the three-second interval we had specified. Keep in mind that you may have to modify such information as the name of the LAN interface when you use this program on your system.

This program increments the expression with the following:

@ count++

If the expression is true, then the command(s) will execute. The @count++ is an assignment operator in the form of:

@ variable_name operator expression

In this case, the variable is first assigned with "=" and is later auto incremented (++). There are a number of operations that can be performed on the variable, as described in Table 28-2:

Table 28-2. Assignment Operators

Operation

Symbol

Example with count = 100

Result

store value

=

@count=100

100

auto increment

++

@count++

101

auto decrement

--

@count--

99

add value

+=

@count+=50

150

subtract value

-=

@count-=50

50

multiply by value

*=

@count*=2

200

divide by value

/=

@count/2

50

There are also comparison operators, such as the "<" used in the example, as well as arithmetic, bitwise, and logical operators. As you craft more and more shell scripts, you will want to use all these operators.

There are a set of test conditions related to files that are useful when writing shell scripts that use files. Using the format - operator filename , you can use the tests in Table 28-3.

Table 28-3. Operator File Name Tests

Operator

Meaning

r

read access

w

write access

x

execute access

o

ownership

z

zero length

f

file, not a directory

d

directory, not a file

The following program, called filetest, uses these operators to test the file .profile . Because .profile is not executable, of zero length, or a directory, I would expect filetest to find these false.

Here is a long listing of .profile :

 martyp $  ls -al .profile  -rw-r--r--   1 martyp   staff        594 May 21 09:29 ../.profile martyp $ 

Here is a listing of the shell script filetest :

 #!/bin/csh #  Program to test file  if (-e ) then              echo " exists"              else              echo " does not exist" endif if (-z ) then              echo " is zero length"              else              echo " is not zero length" endif if (-f ) then              echo " is a file"              else              echo " is not a file" endif if (-d ) then              echo " is a directory"              else              echo " is not a directory" endif if (-o ) then              echo "you own  "              else              echo "you don't own  " endif if (-r ) then              echo " is readable"              else              echo " is not readable" endif if (-w ) then              echo " is writable"              else              echo " is not writable" endif if (-x ) then              echo " is executable"              else              echo " is not executable" endif 

This is a somewhat extreme example of testing a file; however, I wanted to include many of the file tests.

Here is the output of filetest using .profile as input:

 martyp $  filetest /home/martyp/.profile  /home/martyp/.profile exists /home/martyp/.profile is not zero length /home/martyp/.profile is a file /home/martyp/.profile is not a directory you own /home/martyp/.profile /home/martyp/.profile is readable /home/martyp/.profile is writable /home/martyp/.profile is not executable martyp $ 

The result of having run filetest on .profile produces the file test results that we expect.

The next section covers a way of making decisions with switch .

Decision Making with switch

You can use switch to make decisions within a shell program. You can use switch to test command-line arguments or interactive input to shell programs as shown in the upcoming example. If, for example, you wanted to create a menu in a shell program and you needed to determine which option a user selected when running this shell program, you can use switch .

The syntax of switch looks like the following:

 switch (pattern_to_match)   case pattern1              commands              breaksw   case pattern2             commands             breaksw   case pattern 3            commands            breaksw     default            commands            breaksw endsw 

pattern_to_match is the user input that you are testing, and if it is equal to pattern1, then the commands under pattern1 are executed. If pattern_to_match and pattern2 are the same, then the commands under pattern2 will be executed, and so on. If no match occurs between pattern_to_match and one of the case statement patterns, then the default is executed. The following program allows you to pick from between two scripts on its menu. These are the two shell programs we crafted earlier in this chapter. You can expand this script to include as many of your programs as you wish. This example uses switch :

 #!/bin/csh # Program pickscript to run some of # the C shell scripts we've created # Usage: pickscript echo " ---------------------------------------------" echo "                  Sys Admin Menu              " echo "----------------------------------------------" echo " " echo " 1            netcheck for network interface  " echo " " echo " 2            hostck to check connection      " echo "                to hosts in /etc/hosts        " echo "                                              " echo " ---------------------------------------------" echo " " echo " Please enter your selection -> \c" set pick = $<      # read input which is number of script echo " " switch ($pick)     # and assign to variable pick              case 1         # if 1 was selected execute this              $HOME/cshscripts/netcheck 5              breaksw              case 2         # if 2 was selected, execute this              $HOME/cshscripts/hostck              breaksw              default              echo "Please select 1 or 2 next time"              breaksw endsw 

This program allows us to select from between two scripts to run. Let's take a look at an example of running this program:

 martyp $  pickscript  ---------------------------------------------                    Sys Admin Menu ----------------------------------------------  1          netcheck for network interface  2          hostck to check connection               to hosts in /etc/hosts  ---------------------------------------------  Please enter your selection 1 Name  Mtu  Net/Dest      Address        Ipkts  Ierrs Opkts  Oerrs Collis Queue le0   1500 sunsys        sunsys         314996747 0     17261251 52135 7580952 le0   1500 sunsys        sunsys         314996862 0     17261256 52135 7580952 le0   1500 sunsys        sunsys         314997189 0     17261266 52135 7580952 le0   1500 sunsys        sunsys         314997319 0     17261269 52135 7580952 le0   1500 sunsys        sunsys         314997420 0     17261272 52135 7580952 le0   1500 sunsys        sunsys         314997630 0     17261275 52135 7580952 le0   1500 sunsys        sunsys         314997774 0     17261278 52135 7580952 le0   1500 sunsys        sunsys         314997904 0     17261281 52135 7580952 le0   1500 sunsys        sunsys         314998020 0     17261284 52135 7580952 count has reached 9, run netcheck again to see lan0 status martyp $ 

We selected option 1 when we ran pickscript . You can use this program as the basis for running the many shell programs you may write.

Debugging C Shell Programs

When you begin C shell programming, you'll probably make a lot of simple syntax-related errors. Using the -n option to csh , you can have the C shell check the syntax of your program without executing it. I also use the -v option to produce a verbose output. This can sometimes lead to too much information, so I start with -v and if there is too much feedback results, I eliminate it.

The following example is the earlier search1 program, which includes a check that three arguments have been provided. When checking to see that $#argv is equal to 3, I left off the right parenthesis. Here is the listing of the program and a syntax check showing the error:

 martyp $  cat search1  #!/bin/csh # search # Usage: search directory pattern files if ($#argv !3 3 then             # if < 3 args provided         echo "Usage: search directory pattern files"                                 # then print Usage else         echo " "                # else print line and         cd                    # change to search dir         grep -n ""          # search for  in          echo " "                # print line endif martyp $  csh -nv search1  if ( $#argv != 3 then Too many ('s martyp $ 

The csh -nv has performed a syntax check with verbose output. First, the line in question is printed and then an error message that tells you what is wrong with the line. In this case, it is clear that I have left off a right parenthesis.

After fixing the problem, I can run the program with the -x , which causes all commands to be echoed immediately before execution. The following example shows a run of the search program:

 martyp $  csh -xv search1 shellprogs grep csh_hostck  if ( $#argv != 3 ) then if ( 3 != 3 ) then echo " " echo cd  cd /home/martyp/shellprogs grep -n ""  grep -n grep csh_hostck 25:     /usr/sbin/ping $sys 64 1  grep "is alive" > /dev/null echo " " echo endif endif martyp $ 
graphics/grepc_icon.gif

You can follow what is taking place on a line-by-line basis. The line beginning with 25 is the line in the file csh_hostck that has grep in it, that is, the output you would have received if the program had been run without the -xv options.

I would recommend performing the syntax check ( -n ) with a new shell program, and then echo all commands with the -x option only if you get unexpected results when you run the program. The debugging options will surely help you at some point when you run into problems with the shell programs you craft.

How Long Does It Take?

You can use the time command to see a report of the amount of time your shell program takes to run. The output of time is different for many UNIX variants. You may want to view the manual page for time to see what output you can expect. A typical output of time when in csh is shown in Figure 28-5:

Figure 28-5. time Example (different among UNIX variants)

graphics/28fig05.gif

Because some of the scripts you write may consume a substantial amount of system resources, you may want to consider investigating some of the job-control capabilities of the C shell. The simplest job control you can use is to run scripts in the background so that the priority of the script is low. By issuing the script name followed by the & (ampersand), you will run the script in the background. If you run several scripts in the background, you can get the status of these by issuing the jobs command. This is a more advanced C shell topic, but depending on the level of complexity of scripts you write, you may want to look into job control.



HP-UX 11i Systems Administration Handbook and Toolkit
HP-UX 11i Systems Administration Handbook and Toolkit (2nd Edition)
ISBN: 0131018833
EAN: 2147483647
Year: 2003
Pages: 301

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net