Chapter 9. The C Shell

CONTENTS
  •  9.1 The Interactive C Shell
  •  9.2 Programming with the C Shell
  •  C SHELL LAB EXERCISES

graphics/ch09.gif

9.1 The Interactive C Shell

Before the C shell displays a prompt, it is preceded by a number of processes. See Figure 9.1.

Figure 9.1. System startup and the C shell.

graphics/09fig01.gif

9.1.1 Startup

After the system boots, the first process to run is called init; it is assigned process identification number (PID) 1. It gets instructions from a file called inittab (System V) or spawns a getty process (BSD). These processes are responsible for opening up the terminal ports, for providing a place where input comes from (stdin), where standard output (stdout) and error (stderr) go, and for putting a login prompt on your screen. The /bin/login program is then executed. The login program prompts for a password, encrypts and verifies your password, sets up an initial working environment, and then initiates the shell, /bin/csh. The C shell looks in the user's home directory for a file called .cshrc, an initialization file allowing you to customize the C shell environment you will be working in. After executing commands in the .cshrc file, commands in the .login file are executed. The .cshrc file will be executed every time a new C shell is started. The .login file is executed only once when the user logs on, and also contains commands and variables to initialize the user's environment. After executing commands from those files, the percent sign prompt appears on your screen and the C shell awaits commands.

9.1.2 The Environment

Initialization Files. After the csh program starts, it is programmed to execute two files in the user's home directory: the .cshrc file and the .login file. These files allow users to initialize their own environments.

The .cshrc File. The .cshrc file contains C shell variable settings and is executed every time a csh subshell is started. Aliases and history are normally set here.

Example 9.1
(The .cshrc File) 1   if ( $?prompt ) then 2      set prompt = "\! stardust > " 3      set history = 32 4      set savehist = 5 5      set noclobber 6      set filec fignore = ( .o ) 7      set cdpath = ( /home/jody/ellie/bin /usr/local/bin /usr/bin ) 8      set ignoreeof 9      alias m more        alias status 'date;du -s'        alias cd 'cd \!*;set prompt = "\! <$cwd> "'     endif 

EXPLANATION

  1. If the prompt has been set ($?prompt), the shell is running interactively; i.e., it is not running in a script.

  2. The primary prompt is set to the number of the current history event, the name stardust, and a > character. This will change the % prompt, the default.

  3. The history variable is set to 32. This controls the number of history events that will appear on the screen. The last 32 commands you entered will be displayed when you type history (see "Command Line History").

  4. Normally, when you log out, the history list is cleared. The savehist variable allows you to save a specified number of commands from the end of the history list. In this example, the last 5 commands will be saved in a file in your home directory, the .history file, so that when you log in again, the shell can check to see if that file exists and put the history lines saved at the top of the new history list.

  5. The noclobber variable is set to protect the user from inadvertently removing files when using redirection. For example, sort myfile > myfile will destroy myfile. With noclobber set, the message file exists will appear on the screen.

  6. The filec variable is used for filename completion so that you only need to type the first number of significant characters in a filename, press the ESC key, and the shell will complete the rest of the filename. By pressing ^D (Control-D) when typing in the filename, the C shell will display a list of files that match that string. The fignore variable allows you to exclude files that you do not want affected by filename completion. In this case, all the .o filenames (object files) will not be affected by filec, even though filec is set (see "Filename Completion: The filec Variable" on page 355).

  7. The cdpath variable is assigned a list of path elements. When changing directories, if you specify just the directory name, and that directory is not a subdirectory directly below the current working directory, the shell will search the cdpath directory entries to see if it can find the directory in any of those locations and then will change the directory.

  8. The ignoreeof variable prevents you from logging out with ^D. UNIX utilities that accept input from the keyboard, such as the mail program, are terminated by pressing ^D. Often, on a slow system, the user will be tempted to press ^D more than once. The first time, the mail program would be terminated, the second time, the user is logged out. By setting ignoreeof, you are required to type logout to log out.

  9. The aliases are set to give a shorthand notation for a single command or group of commands. Now when you type the alias, the command(s) assigned to it will be executed. The alias for the more command is m. Every time you type m, the more command is executed. The status alias prints the date and a summary of the user's disk usage. The cd alias creates a new prompt every time the user changes directory. The new prompt will contain the number of the current history event (\!*) and the current working directory, $cwd surrounded by < >. (see "Aliases").

The .login File. The .login file is executed one time when you first log in. It normally contains environment variables and terminal settings. It is the file where window applications are usually started. Since environment variables are inherited by processes spawned from this shell and only need to be set once, and terminal settings do not have to be reset for every process, those settings belong in the login file.

Example 9.2
(The .login File) 1   stty -istrip 2   stty erase ^H 3   #     # If possible start the windows system.     # Give a user a chance to bail out     # 4   if ( 'tty' == "/dev/console" ) then 5       if ( $TERM == "sun" || $TERM == "AT386" ) then 6              if ( ${?OPENWINHOME} == 0 ) then 7                   setenv OPENWINHOME /usr/openwin 8              endif            echo "" 9          echo -n "Starting OpenWindows in 5 seconds\                (type Control-C to interrupt)" 10         sleep 5            echo "" 11         $OPENWINHOME/bin/openwin 12         clear 13         logout        endif     endif 

EXPLANATION

  1. The stty command sets options for the terminal. Input characters will not be stripped to seven bits if -istrip is an option.

  2. The stty command sets the Backspace key (Control-H) to erase characters.

  3. Any line beginning with a # is a comment. It is not an executable statement.

  4. If the current terminal window (tty) is the console (/dev/console), the next line is executed; otherwise, program control goes to the last endif.

  5. If the value of the TERM variable is equal to sun or AT386, then the next line is executed.

  6. If the OPENWINHOME environment variable has not been set ($? is 0 if not set, and 1 if set), the next line is executed.

  7. The OPENWINHOME environment variable is set to /usr/openwin.

  8. This endif ends the if on line 5.

  9. The line is displayed on the screen, letting the user know that OpenWindows is starting unless Control-C is typed within the next 5 seconds.

  10. The program sleeps (stops execution) for 5 seconds.

  11. The openwin program is started. A set of terminal windows (shell and command tool windows and a console window) will appear on the screen.

  12. After closing windows, the screen will be cleared.

  13. The user will be logged out.

The Search Path. The path variable is used by the shell to locate commands typed at the command line. The search is from left to right. The dot represents the current working directory. If the command is not found in any of the directories listed in the path, or in the present working directory, the shell sends the message Command not found to standard error. It is recommended that the path be set in the .login file.[1] The search path is set differently in the C shell than it is in the Bourne and Korn shells. Each of the elements is separated by whitespace.

set path = (/usr/bin /usr/ucb /bin /usr .) echo $path /usr/bin /usr/ucb /bin /usr . 

The environment variable PATH will display as:

echo $PATH /usr/bin:/usr/ucb:/bin:. 

The C shell internally updates the environment variable for PATH to maintain compatibility with other programs (such as the Bourne or Korn shells) that may be started from this shell and will need to use the path variable.

The rehash Command. The shell builds an internal hash table consisting of the contents of the directories listed in the search path. (If the dot is in the search path, the files in the dot directory, the current working directory, are not put in the hash table.) For efficiency, the shell uses the hash table to find commands that are typed at the command line, rather than searching the path each time. If a new command is added to one of the directories already listed in the search path, the internal hash table must be recomputed. This is done by typing

% rehash 

(The % is the C shell prompt.) The hash table is also automatically recomputed when you change your path at the prompt or start another shell.

The hashstat Command. The hashstat command displays performance statistics to show the effectiveness of its search for commands from the hash table. The statistics are in terms of "hits" and "misses." If the shell finds most of its commands you use at the end of your path, it has to work harder than if they were at the front of the path, resulting in a higher number of misses than hits. In such cases, you can put the most heavily hit directory toward the front of the path to improve performance.

% hashstat 2 hits, 13 misses, 13% 

The source Command. The source command is a shell built-in command, that is, part of the shell's internal code. It is used to execute a command or set of commands from a file. Normally, when a command is executed, the shell forks a child process to execute the command, so that any changes made will not affect the original shell, called the parent shell. The source command causes the program to be executed in the current shell, so that any variables set within the file will become part of the environment of the current shell. The source command is normally used to reexecute the .cshrc or .login if either has been modified. For example, if the path is changed after logging in, type

% source .login or .cshrc 

The Shell Prompts. The C shell has two prompts: the primary prompt, a percent sign (%), and the secondary prompt, a question mark (?). The primary prompt is the one displayed on the terminal after you have logged in; it waits for you to type commands. The primary prompt can be reset. If you are writing scripts at the prompt that require C shell programming constructs, for example, decision-making or looping, the secondary prompt will appear so that you can continue onto the next line. It will continue to appear after each newline, until the construct has been properly terminated. The secondary prompt cannot be reset.

The Primary Prompt. When running interactively, the prompt waits for you to type a command and press the Enter key. If you do not want to use the default prompt, reset it in the .cshrc file and it will be set for this and all other C subshells. If you only want it set for this login session, set it at the shell prompt.

Example 9.3
1   % set prompt = "$LOGNAME > " 2   ellie > 

EXPLANATION

  1. The primary prompt is assigned the user's login name, followed by a > symbol and a space.

  2. The new prompt is displayed.

The Secondary Prompt. The secondary prompt appears when you are writing on-line scripts at the prompt. Whenever shell programming constructs are entered, followed by a newline, the secondary prompt appears and continues to appear until the construct is properly terminated. Writing scripts correctly at the prompt takes practice. Once the command is entered and you press Enter, you cannot back up, and the C shell history mechanism does not save commands typed at the secondary prompt.

Example 9.4
1   % foreach pal (joe tom ann) 2   ? mail $pal < memo 3   ? end 4   % 

EXPLANATION

  1. This is an example of on-line scripting. Because the C shell is expecting further input after the foreach loop is entered, the secondary prompt appears. The foreach loop processes each word in the parenthesized list.

  2. The first time in the loop, joe is assigned to the variable pal. The user joe is sent the contents of memo in the mail. Then next time through the loop, tom is assigned to the variable pal, and so on.

  3. The end statement marks the end of the loop. When all of the items in the parenthesized list have been processed, the loop ends and the primary prompt is displayed.

  4. The primary prompt is displayed.

9.1.3 The Command Line

After logging in, the C shell displays its primary prompt, by default a percent sign. The shell is your command interpreter. When the shell is running interactively, it reads commands from the terminal and breaks the command line into words. A command line consists of one or more words (or tokens) separated by whitespace (blanks and/or tabs) and terminated with a newline, generated by pressing the Enter key. The first word is the command, and subsequent words are the command's options and/or arguments. The command may be a UNIX executable program such as ls or pwd, an alias, a built-in command such as cd or jobs, or a shell script. The command may contain special characters, called metacharacters, that the shell must interpret while parsing the command line. If the last character in the command line is a backslash, followed by a newline, the line can be continued to the next line.[2]

The Exit Status. After a command or program terminates, it returns an exit status to the parent process. The exit status is a number between 0 and 255. By convention, when a program exits, if the status returned is zero, the command was successful in its execution. When the exit status is nonzero, the command failed in some way. The C shell status variable is set to the value of the exit status of the last command that was executed. Success or failure of a program is determined by the programmer who wrote the program.

Example 9.5
1 % grep "ellie" /etc/passwd     ellie:GgMyBsSJavd16s:9496:40:Ellie Quigley:/home/jody/ellie 2 % echo $status     0 3 % grep "nicky" /etc/passwd 4   % echo $status     1 5   % grep "scott" /etc/passsswd     grep: /etc/passsswd: No such file or directory 6   % echo $status     2 

EXPLANATION

  1. The grep program searches for the pattern ellie in the /etc/passwd file and is successful. The line from /etc/passwd is displayed.

  2. The status variable is set to the exit value of the grep command; 0 indicates success.

  3. The grep program cannot find user nicky in the /etc/passwd file.

  4. The grep program cannot find the pattern, so it returns an exit status of 1.

  5. The grep fails because the file /etc/passsswd cannot be opened.

  6. Grep cannot find the file, so it returns an exit status of 2.

Command Grouping. A command line can consist of multiple commands. Each command is separated by a semicolon and the command line is terminated with a newline.

Example 9.6
% ls; pwd; cal 2001 

EXPLANATION

The commands are executed from left to right until the newline is reached.

Commands may also be grouped so that all of the output is either piped to another command or redirected to a file. The shell executes commands in a subshell.

Example 9.7
1  % ( ls ; pwd; cal 2001 ) > outputfile 2  % pwd; ( cd / ; pwd ) ; pwd    /home/jody/ellie    /    /home/jody/ellie 

EXPLANATION

  1. The output of each of the commands is sent to the file called outputfile. Without the parentheses, the output of the first two commands would go to the screen, and only the output of the cal command would be redirected to the output file.

  2. The pwd command displays the present working directory. The parentheses cause the commands enclosed within them to be processed by a subshell. The cd command is built into the shell. While in the subshell, the directory is changed to root and the present working directory is displayed. When out of the subshell, the present working directory of the original shell is displayed.

Conditional Execution of Commands. With conditional execution, two command strings are separated by two special metacharacters, double ampersand and double vertical (&& and ||). The command on the right of either of these metacharacters will or will not be executed based on the exit condition of the command on the left.

Example 9.8
% grep '^tom:' /etc/passwd && mail tom < letter 

EXPLANATION

If the first command is successful (has a zero exit status), the second command after the && is executed. If the grep command successfully finds tom in the passwd file, the command on the right will be executed: The mail program will send tom the contents of the letter file.

Example 9.9
% grep '^tom:' /etc/passwd || echo "tom is not a user here." 

EXPLANATION

If the first command fails (has a nonzero exit status), the second command after the || is executed. If the grep command does not find tom in the passwd file, the command on the right will be executed: The echo program will print tom is not a user here. to the screen.

Commands in the Background. Normally, when you execute a command, it runs in the foreground, and the prompt does not reappear until the command has completed execution. It is not always convenient to wait for the command to complete. By placing an ampersand at the end of the command line, the shell will return the shell prompt immediately so that you do not have to wait for the last command to complete before starting another one. The command running in the background is called a background job and its output will be sent to the screen as it processes. It can be confusing if two commands are sending output to the screen concurrently. To avoid confusion, you can send the output of the job running in the background to a file or pipe it to another device such as a printer. It is often handy to start a new shell window in the background, so you will have access to both the window from which you started and the new shell window.

Example 9.10
1   % man xview | lp& 2   [1] 1557 3   % 

EXPLANATION

  1. The output from the man pages for the xview program is piped to the printer. The ampersand at the end of the command line puts the job in the background.

  2. There are two numbers that appear on the screen: The number in square brackets indicates that this is the first job to be placed in the background, and the second number is the PID of this job.

  3. The shell prompt appears immediately. While your program is running in the background, the shell is prompting you for another command in the foreground.

9.1.4 Command Line History

The history mechanism is built into the C shell. It keeps a numbered list of the commands (called history events) that you have typed at the command line. You can recall a command from the history list and reexecute it without retyping the command. The history substitution character, the exclamation point, is often called the bang character. The history built-in command displays the history list.

Example 9.11
(The Command Line) % history 1 cd 2 ls 3 more /etc/fstab 4 /etc/mount 5 sort index 6 vi index 

EXPLANATION

The history list displays the last commands that were typed at the command line. Each event in the list is preceded with a number.

Setting History. The C shell history variable is set to the number of events you want to save from the history list and display on the screen. Normally, this is set in the cshrc file, the user's initialization file.

Example 9.12
set history=50 

EXPLANATION

The last 50 commands typed at the terminal are saved and may be displayed on the screen by typing the history command.

Saving History. To save history events across logins, set the savehist variable. This variable is normally set in the .cshrc file, the user's initialization file.

Example 9.13
set savehist=25 

EXPLANATION

The last 25 commands from the history list are saved and will be at the top of the history list the next time you log in.

Displaying History. The history command displays the events in the history list. The history command also has options that control the number of events and the format of the events that will be displayed. The numbering of events does not necessarily start at one. If you have 100 commands on the history list, and you have set the history variable to 25, you will only see the last 25 commands saved.

Example 9.14
% history 1 ls 2 vi file1 3 df 4 ps  eaf 5 history 6 more /etc/passwd 7 cd 8 echo $USER 9 set 

EXPLANATION

The history list is displayed. Each command is numbered.

Example 9.15
% history  h       # Print without line numbers ls vi file1 df ps  eaf history more /etc/passwd cd echo $USER set history  n 

EXPLANATION

The history list is displayed without line numbers.

Example 9.16
% history  r       # Print the history list in reverse 11 history  r 10 history  h  9 set  8 echo $USER  7 cd  6 more /etc/passwd  5 history  4 ps  eaf  3 df  2 vi file1  1 ls 

EXPLANATION

The history list is displayed in reverse order.

Example 9.17
% history 5        # Prints the last 5 events on the history list 7   echo $USER 8   cd 9   set 10  history  n 11  history 5 

EXPLANATION

The last five commands on the history list are displayed.

Reexecuting Commands. To reexecute a command from the history list, the exclamation point (bang) is used. If you type two exclamation points (!!), the last command is reexecuted. If you type the exclamation point followed by a number, the number is associated with the command from the history list and the command is executed. If you type an exclamation point and a letter, the last command that started with that letter is executed. The caret (^) is also used as a shortcut method for editing the previous command.

Example 9.18
1   % date     Mon Feb  8 12:27:35 PST 2001 2   % !!     date     Mon Feb  8 12:28:25 PST 2001 3   % !3     date     Mon Feb  8 12:29:26 PST 2001 4   % !d     date     Mon Feb  8 12:30:09 PST 2001 5   % dare     dare: Command not found. 6   % ^r^t     date     Mon Feb  8 12:33:25 PST 2001 

EXPLANATION

  1. The UNIX date command is executed at the command line. The history list is updated. This is the last command on the list.

  2. The !! (bang bang) gets the last command from the history list; the command is reexecuted.

  3. The third command on the history list is reexecuted.

  4. The last command on the history list that started with the letter d is reexecuted.

  5. The command is mistyped.

  6. The carets are used to substitute letters from the last command on the history list. The first occurrence of an r is replaced with a t.

Example 9.19
1   % cat  file1 file2 file3        <Contents of files displayed here>     % vi !:1     vi file1 2   % cat file1 file2 file3        <Contents of file, file2, and file3 are displayed here>     % ls !:2     ls file2     file2 3   % cat file1 file2 file3     % ls  !:3     ls file3     file3 4   % echo a b c     a b c     % echo !$     echo c     c 5   % echo a b c     a b c     % echo !^     echo a     a 6   % echo a b c     a b c     % echo !*     echo a b c     a b c 7   % !!:p     echo a b c 

EXPLANATION

  1. The cat command displays the contents of file1 to the screen. The history list is updated. The command line is broken into words, starting with word number zero. If the word number is preceded by a colon, that word can be extracted from the history list. The !:1 notation means, get the first argument from the last command on the history list and replace it in the command string. The first argument from the last command is file1. (Word 0 is the command itself.)

  2. The !:2 is replaced with the second argument of the last command, file2, and given as an argument to ls. file2 is printed. (file2 is the third word.)

  3. ls !:3 reads, go to the last command on the history list and get the fourth word (words start at zero) and pass it to the ls command as an argument (file3 is the fourth word).

  4. The bang (!) with the dollar sign ($) refers to the last argument of the last command on the history list. The last argument is c.

  5. The caret (^) represents the first argument after the command. The bang (!) with the ^ refers to the first argument of the last command on the history list. The first argument of the last command is a.

  6. The asterisk (*) represents all arguments after the command. The bang (!) with the * refers to all of the arguments of the last command on the history list.

  7. The last command from the history list is printed but not executed. The history list is updated. You could now perform caret substitutions on that line.

9.1.5 Aliases

An alias is a C shell user-defined abbreviation for a command. Aliases are useful when a command has a number of options and arguments or the syntax is difficult to remember. Aliases set at the command line are not inherited by subshells. Aliases are normally set in the .cshrc file. Since the .cshrc is executed when a new shell is started, any aliases set there will get reset for the new shell. Aliases may also be passed into shell scripts, but will cause potential portability problems unless they are directly set within the script.

Listing Aliases. The alias built-in command lists all set aliases. The alias is printed first, followed by the real command or commands it represents.

Example 9.20
% alias co      compress cp      cp  i ls1     enscript  B  r  Porange  f Courier8 !* & mailq   /usr/lib/sendmail  bp mroe    more mv      mv  i rn      /usr/spool/news/bin/rn3 uc      uncompress uu      uudecode vg      vgrind  t  s11 !:1 | lpr  t weekly  (cd /home/jody/ellie/activity; ./weekly_report; echo Done) 

EXPLANATION

The alias command lists the alias (nickname) for the command in the first column and the real command the alias represents in the second column.

Creating Aliases. The alias command is used to create an alias. The first argument is the name of the alias, the nickname for the command. The rest of the line consists of the command or commands that will be executed when the alias is executed. Multiple commands are separated by a semicolon, and commands containing spaces and metacharacters are surrounded by single quotes.

Example 9.21
1   % alias m more 2   % alias mroe more 3   % alias lF 'ls -alF' 4   % alias cd  'cd \!*; set prompt = "$cwd   >"'     % cd ..     /home/jody >  cd /         # New prompt displayed     / > 

EXPLANATION

  1. The nickname for the more command is set to m.

  2. The alias for the more command is set to mroe. This is handy if you can't spell.

  3. The alias definition is enclosed in quotes because of the whitespace. The alias lF is a nickname for the command ls alF.

  4. When cd is executed, the alias for cd will cause cd to go to the directory named as an argument and will then reset the prompt to the current working directory followed by the string >. The !* is used by the alias in the same way it is used by the history mechanism. The backslash prevents the history mechanism from evaluating the !* first before the alias has a chance to use it. The \!* represents the arguments from the most recent command on the history list.

Deleting Aliases. The unalias command is used to delete an alias. To temporarily turn off an alias, the alias name is preceded by a backslash.

Example 9.22
1   % unalias mroe 2   % \cd .. 

EXPLANATION

  1. The unalias command deletes the alias mroe from the list of defined aliases.

  2. The alias cd is temporarily turned off for this execution of the command only.

Alias Loop. An alias loop occurs when an alias definition references another alias that references back to the original alias.

Example 9.23
1   % alias m more 2   % alias mroe m 3   % alias m mroe         # Causes a loop 4   % m datafile     Alias loop. 

EXPLANATION

  1. The alias is m. The alias definition is more. Every time m is used, the more command is executed.

  2. The alias is mroe. The alias definition is m. If mroe is typed, the alias m is invoked and the more command is executed.

  3. This is the culprit. If alias m is used, it invokes alias mroe, and alias mroe references back to m, causing an alias loop. Nothing bad happens. You just get an error message.

  4. Alias m is used. It is circular. M calls mroe and mroe calls m, then m calls mroe, etc., etc. Rather than looping forever, the C shell catches the problem and displays an error message.

9.1.6 Job Control

Job control is a powerful feature of the C shell that allows you to run programs, called jobs, in the background or foreground. Normally, a command typed at the command line runs in the foreground, and will continue until it has finished. If you have windows, job control may not be necessary, since you can simply open another window to start a new task. On the other hand, with a single terminal, job control is a very useful feature. For a list of job commands, see Table 9.1.

Table 9.1. Job Control Commands
Command Meaning
jobs Lists all the jobs running.
^Z (Control-Z) Stops (suspends) the job; the prompt appears on the screen.
bg Starts running the stopped job in the background.
fg Brings a background job to the foreground.
kill Sends the kill signal to a specified job.

The Ampersand and Background Jobs. If you expect a command to take a long time to complete, you can append the command with an ampersand and the job will execute in the background. The C shell prompt returns immediately and now you can type another command. Now the two commands are running concurrently, one in the background and one in the foreground. They both send their standard output to the screen. If you place a job in the background, it is a good idea to redirect its output either to a file or pipe it to a device such as a printer.

Example 9.24
1   % find . -name core -exec rm {} \; & 2   [1]  543 3   % 

EXPLANATION

  1. The find command runs in the background. (Without the print option, the find command does not send any output to the screen).[3]

  2. The number in square brackets indicates this is the first job to be run in the background and the PID for this process is 543.

  3. The prompt returns immediately. The shell waits for user input.

The Suspend Key Sequence and Background Jobs. To suspend a program, the suspend key sequence, ^Z, is issued. The job is now suspended (stopped), the shell prompt is displayed, and the program will not resume until the fg or bg commands are issued. (When using the vi editor, the ZZ command writes and saves a file. Do not confuse this with ^Z, which would suspend the vi session.) If you try to log out when a job is suspended, the message There are stopped jobs appears on the screen.

The jobs Command. The C shell built-in command, jobs, displays the programs that are currently active and either running or suspended in the background. Running means the job is executing in the background. When a job is stopped, it is suspended; it is not in execution. In both cases, the terminal is free to accept other commands.

Example 9.25
(The Command Line) 1   % jobs 2   [1] + Stopped vi filex     [2] - Running sleep 25 3   % jobs -l     [1] + 355  Stopped vi filex     [2] - 356  Running sleep 25 4   [2] Done sleep 25 

EXPLANATION

  1. The jobs command lists the currently active jobs.

  2. The notation [1] is the number of the first job; the plus sign indicates that the job is not the most recent job to be placed in the background; the dash indicates that this is the most recent job put in the background; Stopped means that this job was suspended with ^Z and is not currently active.

  3. The l option (long listing) displays the number of the job as well as the PID of the job. The notation [2] is the number of the second job, in this case, the last job placed in the background. The dash indicates that this is the most recent job. The sleep command is running in the background.

  4. After sleep has been running for 25 seconds, the job will complete and a message saying that it has finished appears on the screen.

The Foreground and Background Commands. The fg command brings a background job into the foreground. The bg command starts a suspended job running in the background. A percent sign and the number of a job can be used as arguments to fg and bg if you want to select a particular job for job control.

Example 9.26
1   % jobs 2   [1] + Stopped vi filex     [2] - Running cc prog.c -o prog 3   % fg %1     vi filex     (vi session starts) 4   % kill %2     [2] Terminated cc prog.c -o prog 5   % sleep 15     (Press ^z)     Stopped 6   % bg     [1] sleep 15 &     [1] Done   sleep 15 

EXPLANATION

  1. The jobs command lists currently running processes, called jobs.

  2. The first job stopped is the vi session, the second job is the cc command.

  3. The job numbered [1] is brought to the foreground. The number is preceded with a percent sign.

  4. The kill command is built-in. It sends the TERM (terminate) signal, by default, to a process. The argument is either the number or the PID of the process.

  5. The sleep command is stopped by pressing ^Z. The sleep command is not using the CPU and is suspended in the background.

  6. The bg command causes the last background job to start executing in the background. The sleep program will start the countdown in seconds before execution resumes.[4]

9.1.7 Metacharacters

Metacharacters are special characters that are used to represent something other than themselves. As a rule of thumb, characters that are neither letters nor numbers may be metacharacters. The shell has its own set of metacharacters, often called shell wildcards. Shell metacharacters can be used to group commands together, to abbreviate filenames and pathnames, to redirect and pipe input/output, to place commands in the background, and so forth. Table 9.2 presents a partial list of shell metacharacters.

Table 9.2. Shell Metacharacters
Metacharacter Purpose Example Meaning
$ Variable substitution set name=Tom echo $name Tom Sets the variable name to Tom; displays the value stored there.
! History substitution !3 Reexecutes the third event from the history list.
* Filename substitution rm * Removes all files.
? Filename substitution ls ?? Lists all two-character files.
[ ] Filename substitution cat f[123] Displays contents of f1, f2, f3.
; Command separator ls;date;pwd Each command is executed in turn.
& Background processing lp mbox& Printing is done in the background. Prompt returns immediately.
> Redirection of output ls > file Redirects standard output to file.
< Redirection of input ls < file Redirects standard input from file.
>& Redirection of output and error ls >& file Redirects both output and errors to file.
>! If noclobber is set, override it ls >! file If file exists, truncate and overwrite it, even if noclobber is set.
>>! If noclobber is set, override it ls >>! file If file does not exist, create it; even if noclobber is set.
( ) Groups commands to be executed in a subshell (ls ; pwd) >tmp Executes commands and sends output to tmp file.
{ } Groups commands to be executed in this shell { cd /; echo $cwd } Changes to root directory and displays current working directory.

Filename Substitution. When evaluating the command line, the shell uses metacharacters to abbreviate filenames or pathnames that match a certain set of characters. The filename substitution metacharacters listed in Table 9.3 are expanded into an alphabetically listed set of filenames. The process of expanding a metacharacter into filenames is also called globbing. Unlike the other shells, when the C shell cannot substitute a filename for the metacharacter it is supposed to represent, the shell reports No match.

Table 9.3. Shell Metacharacters and Filename Substitution
Metacharacter Meaning
* Matches zero or more characters.
? Matches exactly one character.
[abc] Matches one character in the set: a, b, or c.
[a z] Matches one character in the range a to z.
{a, ile,ax} Matches for a character or set of characters.
~ Substitutes the user's home directory for tilde.
\ Escapes or disables the metacharacter.

9.1.8 Expanding the Metacharacters

The shell performs filename substitution by evaluating its metacharacters and replacing them with the appropriate letters or digits in a filename.

The Asterisk. The asterisk matches zero or more characters in a filename.

Example 9.27
1   % ls     a.c b.c abc ab3 file1 file2 file3 file4 file5 2   % echo *     a.c b.c abc ab3 file1 file2 file3 file4 file5 3   % ls  *.c     a.c b.c 4   % rm  z*p     No match. 

EXPLANATION

  1. All the files in the current directory are listed.

  2. The echo program prints all its arguments to the screen. The asterisk (also called a splat) is a wildcard that means, match for zero or more of any characters found in a filename. All the files in the directory are matched and echoed to the screen.

  3. Filenames ending in .c are listed.

  4. Since none of the files in the directory start with z, the shell reports No match.

The Question Mark. The question mark matches exactly one character in a filename.

Example 9.28
1   % ls     a.c b.c abc ab3 file1 file2 file3 file4 file5 2   % ls ???     abc ab3 3   % echo How are you?     No match. 4   % echo How are you\?     How are you? 

EXPLANATION

  1. All the files in the current directory are listed.

  2. The question mark matches for a single-character filename. Any filenames consisting of three characters are listed.

  3. The shell looks for a filename spelled y-o-u followed by one character. There is not a file in the directory that matches these characters. The shell prints No match.

  4. The backslash preceding the question mark is used to turn off the special meaning of the question mark. Now the shell treats the question mark as a literal character.

The Square Brackets. The square brackets match a filename for one character from a set or range of characters.

Example 9.29
1   % ls     a.c b.c abc ab3 file1 file2 file3 file4 file5 file10     file11  file12 2   % ls file[123]     file1 file2 file3 3   % ls [A-Za-z][a-z][1-5]     ab3 4   % ls file1[0-2]     file10 file11 file12 

EXPLANATION

  1. All the files in the current directory are listed.

  2. Filenames starting with file and follwed with a 1, 2, or 3 are matched and listed.

  3. Filenames starting with a letter (either uppercase or lowercase) followed by a lowercase letter, and followed by a number between 1 and 5 are matched and listed.

  4. Filenames starting with file1 and followed by a 0, 1, or 2 are listed.

The Curly Braces. The curly braces ({})match for a character or string of characters in a filename.

Example 9.30
1   % ls     a.c b.c abc ab3 ab4 ab5 file1 file2 file3 file4 file5 foo     faa fumble 2   % ls f{oo,aa,umble}     foo faa fumble 3   % ls a{.c,c,b[3-5]}     a.c ab3 ab4 ab5 

EXPLANATION

  1. All the files in the current directory are listed.

  2. Files starting with f and followed by the strings oo, aa, or umble are listed. Spaces inside the curly braces will cause the error message Missing }.

  3. Files starting with a followed by .c, c, or b3, b4, or b5 are listed. (The square brackets can be used inside the curly braces.)

Escaping Metacharacters. The backslash is used to escape the special meaning of a single character. The escaped character will represent itself.

Example 9.31
1   % gotta light?     No match. 2   % gotta light\?     gotta: Command not found. 

EXPLANATION

  1. This is a little UNIX joke. The question mark is a file substitution metacharacter and evaluates to a single character. The shell looks for a file in the present working directory that contains the characters l-i-g-h-t, followed by a single character. If the shell cannot find the file, it reports No match. This shows you something about the order in which the shell parses the command line. The metacharacters are evaluated before the shell tries to locate the gotta command.

  2. The backslash protects the metacharacter from interpretation, often called escaping the metacharacter. Now the shell does not complain about a No match, but searches the path for the gotta command, which is not found.

Tilde Expansion. The tilde character by itself expands to the full pathname of the user's home directory. When the tilde is prepended to a username, it expands to the full pathname of that user's home directory. When prepended to a path, it expands to the home directory and the rest of the pathname.

Example 9.32
1   % echo ~     /home/jody/ellie 2   % cd ~/desktop/perlstuff     % pwd     /home/jody/ellie/desktop/perlstuff 3   % cd ~joe     % pwd     /home/bambi/joe 

EXPLANATION

  1. The tilde expands to the user's home directory.

  2. The tilde followed by a pathname expands to the user's home directory, followed by /desktop/perlstuff.

  3. The tilde followed by a username expands to the home directory of the user. In this example, the directory is changed to that user's home directory.

Filename Completion: The filec Variable. When running interactively, the C shell provides a shortcut method for typing a filename or username. The built-in filec variable, when set, is used for what is called filename completion. If you type the first few significant characters of a file in the current working directory and press the ESC key, the shell fills in the rest of the filename, provided that there are not a number of other files beginning with the same characters. If you type Control-D after the partial spelling of the file, the shell will print out a list of files that match those characters. The terminal beeps if there are multiple matches. If the list begins with a tilde, the shell attempts to expand that list to a username.

Example 9.33
1   % set filec 2   % ls     rum rumple rumplestilsken run2 3   % ls ru[ESC][5]     # terminal beeps 4   % ls rum^D     rum rumple rumplestilsken 5   % ls rump[ESC]     rumple 6   % echo ~ell[ESC]     /home/jody/ellie 

EXPLANATION

  1. The special C shell variable filec is set. Filename completion can be used.

  2. The files in the present working directory are listed.

  3. Filename completion is attempted. The letters r and u are not unique; that is, the shell does not know which one to pick, so it causes the terminal to beep.

  4. After the letters r-u-m are typed, ^D is pressed. A list of all filenames beginning with rum are displayed.

  5. The first filename starting with rump is completed and displayed.

  6. If a tilde precedes a partially spelled username, the shell will attempt to complete the spelling of the user's name and display the user's home directory.

Turning Off Metacharacters with noglob. If the noglob variable is set, filename substitution is turned off, meaning that all metacharacters represent themselves; they are not used as wildcards. This can be useful when searching for patterns in programs like grep, sed, or awk, which may contain metacharacters that the shell may try to expand.

Example 9.34
1   % set noglob 2   % echo * ?? [] ~     * ?? [] ~ 

EXPLANATION

  1. The variable noglob is set. It turns off the special meaning of the wildcards.

  2. The metacharacters are displayed as themselves without any interpretation.

9.1.9 Redirection and Pipes

Normally, standard output (stdout) from a command goes to the screen, standard input (stdin) comes from the keyboard, and error messages (stderr) go to the screen. The shell allows you to use the special redirection metacharacters to redirect the input/output to or from a file. The redirection operators (<, >, >>, >&) are followed by a filename. This file is opened by the shell before the command on the left-hand side is executed.

Pipes, represented by a vertical bar (|) symbol, allow the output of one command to be sent to the input of another command. The command on the left-hand side of the pipe is called the writer because it writes to the pipe. The command on the right-hand side of the pipe is the reader because it reads from the pipe. See Table 9.4 for a list of redirection and pipe metacharacters.

Table 9.4. Redirection Metacharacters
Metacharacter Meaning
command < file Redirects input from file to command.
command > file Redirects output from command to file.
command >& file Redirects output and errors to file.
command >> file Redirects output of command and appends it to file.
command >>& file Redirects and appends output and errors of command to file.
command << WORD Redirects input from first WORD to terminating WORD to command.
<input> User input goes here. It will be treated as a doubly quoted string of text.
WORD WORD marks the termination of input to command.
command | command Pipes output of first command to input of second command.
command |& command Pipes output and errors of first command to input of second command.
command >! file If the noclobber variable is set, override its effects for this command and either open or overwrite file.
command >>! file Override noclobber variable; if file does not exist, it is created and output from command is appended to it.
command >>&! file Override noclobber variable; if file does not exist, it is created and both output and errors are appended to it.

Redirecting Input. Instead of the input coming from the terminal keyboard, it can be redirected from a file. The shell will open the file on the right-hand side of the < symbol and the program on the left will read from the file. If the file does not exist, the error No such file or directory will be reported by the C shell.

FORMAT

command < file 
Example 9.35
mail bob < memo 

EXPLANATION

The file memo is opened by the shell, and the input is redirected to the mail program. Simply, the user bob is sent a file called memo by the mail program.

The here document. The here document is another way to redirect input to a command. It is used in shell scripts for creating menus and processing input from other programs. Normally, programs that accept input from the keyboard are terminated with Control-D (^d). The here document provides an alternate way of sending input to a program and terminating the input without typing ^D. The << symbol is followed by a user-defined word, often called a terminator. Input will be directed to the command on the left-hand side of the << symbol until the user-defined terminator is reached. The final terminator is on a line by itself, and cannot be surrounded by any spaces. Variable and command substitution are performed within the here document. (Normally, here documents are used in shell scripts to create menus and provide input to commands such as mail, bc, ex, ftp, etc.)

FORMAT

command << MARK        ... input ... MARK 
Example 9.36
(Without the here document) (The Command Line) 1   % cat 2   Hello There.     How are you?     I'm tired of this. 3   ^D (The Output) 4   Hello There.     How are you?     I'm tired of this. 

EXPLANATION

  1. The cat program, without arguments, waits for keyboard input.

  2. The user types input at the keyboard.

  3. The user types ^D to terminate input to the cat program.

  4. The cat program sends its output to the screen.

Example 9.37
(With the here document) (The Command Line) 1   % cat << DONE 2   Hello There.     How are you?     I'm tired of this. 3   DONE (The Output) 4   Hello There.     How are you?     I'm tired of this. 

EXPLANATION

  1. The cat program will receive input from the first DONE to the terminating DONE. The words are user-defined terminators.

  2. These lines are input. When the word DONE is reached, no more input is accepted.

  3. The final terminator marks the end of input. There cannot be any spaces on either side of this word.

  4. The text between the first word DONE and the final word DONE is the output of the cat command (from "here" to "here") and is sent to the screen. The final DONE must be against the left margin with no space or other text to the right of it.

Example 9.38
(The Command Line) 1   % set name = steve 2   % mail $name << EOF 3   Hello there, $name 4   The hour is now 'date +%H' 5   EOF 

EXPLANATION

  1. The shell variable name is assigned the username steve. (Normally, this example would be included in a shell script.)

  2. The variable name is expanded within the here document.

  3. The mail program will receive input until the terminator EOF is reached.

  4. Command substitution is performed within the here document; that is, the command within the backquotes is executed and the output of the command is replaced within the string.

  5. The terminator EOF is reached, and input to the mail program is stopped.

Redirecting Output. By default, the standard output of a command or commands normally goes to the terminal screen. To redirect standard output from the screen to a file, the > symbol is used. The command is on the left-hand side of the > symbol, and a filename is on the right-hand side. The shell will open the file on the right-hand side of the > symbol. If the file does not exist, the shell will create it; if it does exist, the shell will open the file and truncate it. Often files are inadvertently removed when using redirection. (A special C shell variable, called noclobber, can be set to prevent redirection from clobbering an existing file. See Table 9.5.)

FORMAT

command > file 
Example 9.39
cat file1 file2 > file3 

EXPLANATION

The contents of file1 and file2 are concatenated and the output is sent to file3. Remember that the shell opens file3 before it attempts to execute the cat command. If file3 already exists and contains data, the data will be lost. If file3 does not exist, it will be created.

Appending Output to an Existing File. To append output to an existing file, the >> symbol is used. If the file on the right-hand side of the >> symbol does not exist, it is created; if it does exist, the file is opened and output is appended to the end of the file.

FORMAT

command >> file 
Example 9.40
date >> outfile 

EXPLANATION

The standard output of the date command is redirected and appended to outfile.

Redirecting Output and Error. The >& symbol is used to redirect both standard output and standard error to a file. Normally, a command is either successful and sends its output to stdout, or fails and sends its error messages to stderr. Some recursive programs, such as find and du, send both standard output and errors to the screen as they move through the directory tree. By using the >& symbol, both standard output and standard error can be saved in a file and examined. The C shell does not provide a symbol for redirection of only standard error, but it is possible to get just the standard error by executing the command in a subshell. See Figure 9.2.

Figure 9.2. Redirecting stdout and stderr. See Example 9.41.

graphics/09fig02.gif

Example 9.41
1   % date     Tue Aug 3 10:31:56  PDT  2001 2   % date >& outfile 3   % cat outfile     Tue Aug 3 10:31:56  PDT  2001 

EXPLANATION

  1. The output of the date command is sent to standard output, the screen.

  2. The output and errors are sent to outfile.

  3. Since there were no errors, the standard output is sent to outfile and the contents of the file are displayed.

Example 9.42
1   % cp file1 file2 2   % cp file1      Usage: cp [-ip] f1 f2; or: cp [-ipr] f1 ... fn d2 3   % cp file1 >& errorfile 4   % cat errorfile      Usage: cp [-ip] f1 f2; or: cp [-ipr] f1 ... fn d2 

EXPLANATION

  1. To copy a file, the cp command requires both a source file and a destination file. The cp command makes a copy of file1 and puts the copy in file2. Since the cp command is given the correct syntax, nothing is displayed to the screen. The copy was successful.

  2. This time the destination file is missing and the cp command fails, sending an error to stderr, the terminal.

  3. The >& symbol is used to send both stdout and stderr to errorfile. Since the only output from the command is the error message, that is what is saved in errorfile.

  4. The contents of errorfile are displayed, showing that it contains the error message produced by the cp command.

Separating Output and Errors. Standard output and standard error can be separated by enclosing the command in parentheses. When a command is enclosed in parentheses, the C shell starts up a subshell, handles redirection from within the subshell, and then executes the command. By using the technique shown in Example 9.43, the standard output can be separated from the errors.

Example 9.43
(The Command Line) 1   % find . -name ' * .c' -print >& outputfile 2   % (find . -name '*.c' -print > goodstuff) >& badstuff 

EXPLANATION

  1. The find command will start at the current directory, searching for all files ending in .c, and will print the output to outputfile. If an error occurs, that will also go into outputfile.

  2. The find command is enclosed within parentheses. The shell will create a subshell to handle the command. Before creating the subshell, the words outside the parentheses will be processed; that is, the badstuff file will be opened for both standard output and error. When the subshell is started, it inherits the standard input, output, and errors from its parent. The subshell then has standard input coming from the keyboard, and both standard output and standard error going to the badstuff file. Now the subshell will handle the > operator. The stdout will be assigned the file goodstuff. The output is going to goodstuff, and the errors are going to badstuff. See Figure 9.3.

    Figure 9.3. Separating stdout and stderr.

    graphics/09fig03.gif

The noclobber Variable. The special C shell built-in variable noclobber, when set, protects you from clobbering files with redirection. See Table 9.5.

Table 9.5. The noclobber Variable
noclobber Is Not Set File Exists File Does Not Exist
command > file file is overwritten. file is created.
command >> file file is appended to. file is created.
noclobber Is Set
command > file Error message. file is created.
command >> file file is appended to. Error message.
Overwriting noclobber
command >! file If the noclobber variable is set, override its effects for this command and either open or truncate file, redirecting output of command to file.
command >>! file Override noclobber variable; if file does not exist, it is created and output from command is appended to it. (See Example 9.44.)
Example 9.44
1   % cat filex     abc     123 2   % date > filex 3   % cat filex     Wed Aug 5 11:51:04  PDT 2001 4   % set noclobber 5   % date > filex     filex: File exists. 6   % ls >! filex   # Override noclobber for this command only     % cat filex     abc     ab1     dir     filex     plan.c 7   % ls > filex     filex:  File exists. 8   % unset noclobber     # Turn off noclobber permanently 

EXPLANATION

  1. The contents of filex are displayed on the screen.

  2. The output of the date command is redirected to filex. The file is truncated and its original contents overwritten.

  3. The contents of filex are displayed.

  4. The noclobber variable is set.

  5. Since filex already exists and noclobber is set, the shell reports that the file exists and will not allow it to be overwritten.

  6. The output of ls is redirected to filex because the >! operator overrides the effects of noclobber.

  7. The effects of the >! symbol were temporary. It does not turn off noclobber. It simply overrides noclobber for the command where it is implemented.

  8. The noclobber variable is unset.

9.1.10 Variables

C shell variables hold only strings or a set of strings. Some variables are built into the shell and can be set by turning them on or off, such as the noclobber or filec variable. Others are assigned a string value, such as the path variable. You can create your own variables and assign them to strings or the output of commands. Variable names are case-sensitive and may contain up to 20 characters consisting of numbers, letters, and the underscore.

There are two types of variables: local and environment. The scope of a variable is its visibility. A local variable is visible to the shell where it is defined. The scope of environment variables is often called global. Their scope is for this shell and all processes spawned (started) from this shell.

The dollar sign ($) is a special metacharacter that, when preceding a variable name, tells the shell to extract the value of that variable. The echo command, when given the variable as an argument, will display the value of the variable after the shell has processed the command line and performed variable substitution.

The special notation $?, when prepended to the variable name, lets you know whether the variable has been set. If a one is returned, it means true, the variable has been set. If a zero is returned, it means false, the variable has not been set.

Example 9.45
1   % set filec 2   % set history = 50 3   % set name = George 4   % set machine = 'uname -n' 5   % echo  $?machine     1 6   % echo  $?blah     0 

EXPLANATION

  1. Sets the built-in variable filec for filename completion.

  2. Sets the built-in variable history to 50 to control the number of events displayed.

  3. Sets the user-defined variable name to George.

  4. Sets the user-defined variable machine to the output of the UNIX command. The command is in backquotes, telling the shell to perform command substitution.

  5. The $? is prepended to the variable name to test whether or not the variable has been set. Since the test yields a one (true), the variable has been set.

  6. The $? yields zero (false). The variable has not been set.

Curly Braces. Curly braces insulate a variable from any characters that may follow it.

Example 9.46
1   % set var = net     % echo $var     net 2   % echo $varwork     varwork: Undefined variable. 3   % echo ${var}work     network 

EXPLANATION

  1. The curly braces surrounding the variable name insulate the variable from characters that follow it.

  2. A variable called varwork has not been defined. The shell prints an error message.

  3. The curly braces shield the variable from characters appended to it. $var is expanded and the string work is appended.

Local Variables. Local variables are known only in the shell where they were created. If a local variable is set in the .cshrc file, the variable will be reset every time a new C shell is started. By convention, local variables are named with lowercase letters.

Setting Local Variables. If the string being assigned contains more than one word, it must be quoted; otherwise, only the first word will be assigned to the variable. It does not matter if there are spaces around the equal sign, but if there is a space on one side of the equal sign, there must be one on the other side.

Example 9.47
1   % set round = world 2   % set name = "Santa Claus" 3   % echo $round     world 4   % echo $name     Santa Claus 5   % csh            # Start a subshell 6   % echo $name     name: Undefined variable. 

EXPLANATION

  1. The local variable round is assigned the value world.

  2. The local variable name is assigned the value Santa Claus. The double quotes keep the shell from evaluating the whitespace between Santa and Claus.

  3. The dollar sign prepended to the variable allows the shell to perform variable substitution, that is, to extract the value stored in the variable.

  4. Variable substitution is performed.

  5. A new C shell (called a subshell) process is started.

  6. In the subshell, the variable name has not been defined. It was defined in the parent shell as a local variable.

The set Command. The set command prints all local variables set for this shell.

Example 9.48
(The Command Line) % set argv       () cwd        /home/jody/ellie fignore    .o filec history    500 home       /home/jody/ellie hostname   jody ignoreeof noclobber notify path       (/home/jody/ellie /bin /usr/local /usr/usr/bin/usr/etc .) prompt     jody% shell      /bin/csh status     0 term       sun cmd user       ellie 

EXPLANATION

All of the local variables set for this shell are printed. Most of these variables are set in the cshrc file. The argv, cwd, shell, term, user, and status variables are preset, built-in variables.

Built-In Local Variables. The shell has a number of predefined variables with their own definitions. Some of the variables are either on or off. For example, if you set noclobber, the variable is on and effective, and when you unset noclobber, it is turned off. Some variables require a definition when set. Built-in variables are usually set in the .cshrc file if they are to be effective for different C shells. Some of the built-in variables already discussed include noclobber, cdpath, history, filec, and noglob. For a complete list, see Table 9.16.

Environment Variables. Environment variables are often called global variables. They are defined in the shell where they were created and inherited by all shells spawned from that shell. Although environment variables are inherited by subshells, those defined in subshells are not passed back to parent shells. Inheritance is from parent to child, not the other way around (like real life). By convention, environment variables are named with capital letters.

Example 9.49
(The Command Line) 1   % setenv TERM wyse 2   % setenv PERSON "Joe Jr." 3   % echo $TERM     wyse 4   % echo $PERSON     Joe Jr. 5   % echo $$        # $$ evaluates to the PID of the current shell     206 6   % csh            # Start a subshell 7   % echo $$     211 8   % echo $PERSON     Joe Jr. 9   % setenv PERSON "Nelly Nerd" 10  % echo $PERSON     % Nelly Nerd 11  % exit              # Exit the subshell 12  % echo $$     206 13  % echo $PERSON      # Back in parent shell     Joe Jr. 

EXPLANATION

  1. The shell environment variable TERM is set to a wyse terminal.

  2. The user-defined variable PERSON is set to Joe Jr. The quotes are used to protect the space.

  3. The dollar sign ($) prepended to the variable name allows the shell to evaluate the contents of the variable, called variable substitution.

  4. The value of the environment variable PERSON is printed.

  5. The $$ variable contains the PID of the current shell. The PID is 206.

  6. The csh command starts a new C shell, called a subshell.

  7. The PID of the current shell is printed. Since this is a new C shell, it has a different PID number. The PID is 211.

  8. The environment variable PERSON was inherited by the new shell.

  9. The PERSON variable is reset to Nelly Nerd. This variable will be inherited by any shells spawned from this shell.

  10. The new value of the PERSON variable is printed.

  11. This C shell is exited.

  12. The original C shell is running; to attest to that, the PID 206 is printed. It is the same as it was before the subshell was started.

  13. The PERSON variable contains its original value.

Printing Environment Variables. The printenv (UCB) and env (SVR4) commands print all the environment variables set for this shell and its subshells. The setenv command prints variables and their values on both the UCB and SVR4 versions of the C shell.

Example 9.50
% env FONTPATH=/usr/local/OW3/lib/fonts HELPPATH=/usr/local/OW3/lib/locale:/usr/local/OW3/lib/help HOME=/home/jody/ellie LD_LIBRARY_PATH=/usr/local/OW3/lib LOGNAME=ellie MANPATH=/ur/local/man:/usr/local/man:/usr/local/doctools/man:/usr/man NOSUNVIEW=0 OPENWINHOME=/usr/local/OW3 PATH=/bin:/usr/local:/usr:/usr/bin:/usr/etc:/home/5bin:/usr/    doctools:/usr:. PWD=/home/jody/ellie SHELL=/bin/csh TERM=sun cmd USER=ellie WINDOW_PARENT=/dev/win0 WINDOW_TTYPARMS= WMGR_ENV_PLACEHOLDER=/dev/win3 

EXPLANATION

The environment variables are set for this session and all processes that are started from this shell. Many applications require the setting of environment variables. For example, the man command has a MANPATH variable set to the location that man pages can be found, and the openwin program has an environment variable set to the place where its fonts are stored. When any of these programs are executed, the information in these variables is passed to them.

Arrays. In the C shell, an array is simply a list of words, separated by spaces or tabs, and enclosed in parentheses. The elements of the array are numbered by subscripts starting at one. If there is not an array element for a subscript, the message Subscript out of range is displayed. Command substitution will also create an array. If the $# notation precedes an array name, the number of elements in the array is displayed.

Example 9.51
1   % set fruit = ( apples pears peaches plums ) 2   % echo $fruit     apples pears peaches plums 3   % echo $fruit[1]       # Subscripts start at 1     apples 4   % echo $fruit[2 4]     # Prints the 2nd, 3rd, and 4th elements     pears peaches plums 5   $ echo $fruit[6]     Subscript out of range. 6   % echo $fruit[*]       # Prints all elements of the array     apples pears peaches plums 7   % echo $#fruit         # Prints the number of elements     4 8   % echo $fruit[$#fruit]     # Prints the last element     plums 9   % set fruit[2] = bananas    # Reassigns the second element     % echo $fruit     apples bananas peaches plums 10  % set path = ( ~ /usr/bin /usr /usr/local/bin . )     % echo $path     /home/jody/ellie /usr/bin /usr /usr/local/bin . 11  % echo $path[1]     /home/jody/ellie 

EXPLANATION

  1. The wordlist is enclosed within parentheses. Each word is separated by whitespace. The array is called fruit.

  2. The words in the fruit array are printed.

  3. The first element of the fruit array is printed. The subscripts start at one.

  4. The second, third, and fourth elements of the wordlist are printed. The dash allows you to specify a range.

  5. The array does not have six elements. The subscript is out of range.

  6. All elements of the fruit array are printed.

  7. The $# preceding the array is used to obtain the number of elements in the array. There are four elements in the fruit array.

  8. Since the subscript $#fruit evaluates to the total number of elements in the array, if that value is used as an index value of the array, i.e., [$#fruit], the last element of the fruit array is printed.

  9. The second element of the array is assigned a new value. The array is printed with its replaced value, bananas.

  10. The path variable is a special C shell array of directories used to search for commands. By creating an array, the individual elements of the path can be accessed or changed.

  11. The first element of path is printed.

The shift Command and Arrays. If the built-in shift command takes an array name as its argument, it shifts off (to the left) the first element of the array. The length of the array is decreased by one. (Without an argument, the shift command shifts off the first element of the built-in argv array. See "Command Line Arguments")

Example 9.52
1   % set names = ( Mark Tom Liz Dan Jody ) 2   % echo $names     Mark Tom Liz Dan Jody 3   % echo $names[1]     Mark 4   % shift  names 5   % echo $names     Tom Liz Dan Jody 6   % echo $names[1]     Tom 7   % set days = ( Monday Tuesday ) 8   % shift days 9   % echo $days     Tuesday 10  % shift days 11  % echo $days 12  % shift days     shift: no more words. 

EXPLANATION

  1. The array is called names. It is assigned the list of words in parentheses. Each word is separated by whitespace.

  2. The array is printed.

  3. The first element of the array is printed.

  4. The array is shifted to the left by one element. The word Mark is shifted off.

  5. The array was decreased by one element after the shift.

  6. The first element of the array, after the shift, is Tom.

  7. An array called days is created. It has two elements, Monday and Tuesday.

  8. The array days is shifted one to the left.

  9. The array is printed. Tuesday is the only element left.

  10. The array days is shifted again. The array is empty.

  11. The days array is empty.

  12. This time, attempting to shift causes the shell to send an error message indicating that it cannot shift elements from an empty array.

Creating an Array from a String. You may want to create a wordlist out of a quoted string. This is accomplished by placing the string variable within a set of parentheses.

Example 9.53
1   % set name = "Thomas Ben Savage"     % echo $name[1]     Thomas Ben Savage 2   % echo $name[2]     Subscript out of range. 3   % set name = ( $name ) 4   % echo $name[1] $name[2] $name[3]     Thomas Ben Savage 

EXPLANATION

  1. The variable name is assigned the string Thomas Ben Savage.

  2. When treated as an array, there is only one element, the entire string.

  3. The variable is enclosed in parentheses, creating an array of words, called name.

  4. The three elements of the new array are displayed.

9.1.11 Special Variables

Built into the C shell are several variables consisting of one character. The $ preceding the character allows variable interpretation. See Table 9.6.

Table 9.6. Variables and Their Meanings
Variable Example Meaning
$?var echo $?name Returns 1 if variable has been set, 0 if not.
$#var echo $#fruit Prints the number of elements in an array.
$$ echo $$ Prints the PID of the current shell.
$< set name = $< Accepts a line of input from user up to newline.
Example 9.54
1   % set num     % echo $?num     1 2   % echo $path     /home/jody/ellie   /usr/bin/   usr/local/bin     % echo $#path     3 3   % echo $$     245     % csh              # Start a subshell     % echo $$     248 4   % set name = $<     Christy Campbell     % echo $name     Christy Campbell 

EXPLANATION

  1. The variable num is set to null. The $? preceding the variable evaluates to one if the variable has been set (either to null or some value), and to zero if the variable has not been set.

  2. The path variable is printed. It is an array of three elements. The $# preceding the variable extracts and prints the number of elements in the array.

  3. The $$ is the PID of the current process, in this case, the C shell.

  4. The $< variable accepts a line of input from the user up to, but not including, the newline, and stores the line in the name variable. The value of the name variable is displayed.

Pathname Variable Modifiers. If a pathname is assigned to a variable, it is possible to manipulate the pathname variable by appending special C shell extensions to it. The pathname is divided into four parts: head, tail, root, and extension. See Table 9.7 for examples of pathname modifiers and what they do.

Table 9.7. Pathname Modifiers set pn = /home/ellie/prog/check.c
Modifier Meaning Example Result
:r root echo $pn:r /home/ellie/prog/check
:h head echo $pn:h /home/ellie/prog
:t tail echo $pn:t check.c
:e extension echo $pn:e c
:g global echo $p:gt (See Example 9.55)
Example 9.55
1   % set pathvar = /home/danny/program.c 2   % echo $pathvar:r     /home/danny/program 3   % echo $pathvar:h     /home/danny 4   % echo $pathvar:t     program.c 5   % echo $pathvar:e     c 6   % set pathvar = ( /home/* )     echo $pathvar     /home/jody /home/local /home/lost+found /home/perl /home/tmp 7   % echo $pathvar:gt     jody  local  lost+found  perl tmp 

EXPLANATION

  1. The variable pathvar is set to /home/danny/program.c.

  2. When :r is appended to the variable, the extension is removed when displayed.

  3. When :h is appended to the variable, the head of the path is displayed; that is, the last element of the path is removed.

  4. When :t is appended to the variable, the tail end of the path (the last element) is displayed.

  5. When :e is appended to the variable, the extension is displayed.

  6. The variable is set to /home/*. The asterisk expands to all the pathnames in the current directory starting in /home/.

  7. When :gt is appended to the variable, the tail end of each (global) of the path elements is displayed.

9.1.12 Command Substitution

A string or variable can be assigned the output of a UNIX command by placing the command in backquotes. This is called command substitution. (On the keyboard, the backquote is normally below the tilde character.) If the output of a command is assigned to a variable, it is stored as a wordlist (see "Arrays" on page 370), not a string, so that each of the words in the list can be accessed separately. To access a word from the list, a subscript is appended to the variable name. Subscripts start at one.

Example 9.56
1   % echo The name of my machine is 'uname -n'.     The name of my machine is stardust. 2   % echo The present working directory is 'pwd'.     The present working directory is /home/stardust/john. 3   % set d = 'date'     % echo $d     Sat Jun 20 14:24:21 PDT 2001 4   % echo $d[2] $d[6]     Jun 2001 5   % set d = "'date'"     % echo $d[1]     Sat Jun 20 14:24:21 PDT 2001 

EXPLANATION

  1. The UNIX command uname n is enclosed in backquotes. When the shell encounters the backquotes, it will execute the enclosed command, uname n, and substitute the output of the command, stardust, into the string. When the echo command prints its arguments to standard output, the name of the machine will be one of its arguments.

  2. The UNIX command pwd is executed by the shell and the output is substituted in place within the string.

  3. The local variable d is assigned the output of the date command. The output is stored as a list of words (an array).

  4. Elements 2 and 6 of the d array are printed. The subscripts start at one.

  5. Since the output is enclosed in double quotes, it is a single string rather than a wordlist.

Wordlists and Command Substitution. When a command is enclosed in backquotes and assigned to a variable, the resulting value is an array (wordlist). Each element of the array can be accessed by appending a subscript to the array name. The subscripts start at one. If a subscript that is greater than the number of words in the array is used, the C shell prints Subscript out of range. If the output of a command consists of more than one line, the newlines are stripped from each line and replaced with a single space.

Example 9.57
1   % set d = 'date'     % echo $d     Fri Aug 29 14:04:49 PDT 2001 3   % echo $d[1 3]     Fri Aug 29 4   % echo $d[6]     2001 4   % echo $d[7]     Subscript out of range. 5   % echo The calendar for the month of November is 'cal 11 2001'"     The calendar for month of November is November 2001 S M Tu W     Th F S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21     22 23 24 25 26 27 28 29 30 

EXPLANATION

  1. The variable d is assigned the output of the UNIX date command. The output is stored as an array. The value of the variable is displayed.

  2. The first three elements of the array are displayed.

  3. The sixth element of the array is displayed.

  4. There are not seven elements in the array. The shell reports that the subscript is out of range.

  5. The output spans more than one line. Each newline is replaced with a space. This may not be the output you expected.

Example 9.58
1   % set machine = 'rusers | awk '/tom/{print $1}'' 2   % echo $machine     dumbo bambi dolphin 3   % echo $#machine     3 4   % echo $machine[$#machine]     dolphin 5   % echo $machine     dumbo bambi dolphin 6   % shift $machine     % echo $machine     bambi dolphin 7   % echo $machine[1]     bambi 8   % echo $#machine     2 

EXPLANATION

  1. The output of the rusers command is piped to awk. If the regular expression tom is found, awk prints the first field. The first field, in this case, is the name of the machine(s) where ser tom is logged on.

  2. User tom is logged on three machines. The names of the machines are displayed.

  3. The number of elements in the array is accessed by preceding the array name with $#. There are three elements in the array.

  4. The last element of the array is displayed. The number of elements in the array ($#machine) is used as a subscript.

  5. The array is displayed.

  6. The shift command shifts the array to the left. The first element of the array is dropped and the subscripts are renumbered, starting at one.

  7. The first element of the array after the shift is displayed.

  8. After the shift, the length of the array has decreased by one.

9.1.13 Quoting

The C shell has a whole set of metacharacters that have some special meaning. In fact, almost any character on your keyboard that is not a letter or a number has some special meaning for the shell. Here is a partial list:

* ? [  ] $ ~ ! ^ & {  } (  ) > < | ; : % 

The backslash and quotes are used to escape the interpretation of metacharacters by the shell. Whereas the backslash is used to escape a single character, the quotes can be used to protect a string of characters. There are some general rules for using quotes:

  1. Quotes are paired and must be matched on a line. The backslash character can be used to escape a newline so that a quote can be matched on the next line.

  2. Single quotes will protect double quotes, and double quotes will protect single quotes.

  3. Single quotes protect all metacharacters from interpretation, with the exception of the history character (!).

  4. Double quotes protect all metacharacters from interpretation, with the exception of the history character (!), the variable substitution character ($), and the backquotes (used for command substitution).

The Backslash. The backslash is used to escape the interpretation of a single character and, in the C shell, is the only character that can be used to escape the history character, the exclamation point (also called the bang). Often the backslash is used to escape the newline character. Backslash interpretation does not take place within quotes.

Example 9.59
1   % echo Who are you?     echo: No match. 2   % echo Who are you\?     Who are you? 3   % echo This is a very,very long line and this is where I\     break the line.     This is a very, very long line and this is where I        I break the line. 4   % echo "\\abc"     \\abc     % echo '\\abc'     \\abc     % echo \\abc     \abc 

EXPLANATION

  1. The question mark is used for filename expansion. It matches for a single character. The shell is looking for a file in the current directory that is spelled y-o-u, followed by a single character. Since there is not a file by that name in the directory, the shell complains that it could not find a match with No match.

  2. The shell will not try to interpret the question mark, since it is escaped with the backslash.

  3. The string is continued to the next line by escaping the newline with a backslash.

  4. If the backslash is enclosed in either single or double quotes, it is printed. When not enclosed in quotes, the backslash escapes itself.

Single Quotes. Single quotes must be matched on the same line and will escape all metacharacters with the exception of the history (bang) character (!). The history character is not protected because the C shell evaluates history before it does quotes, but not before backslashes.

Example 9.60
1   % echo 'I need $5.00'     I need $5.00 2   % echo 'I need $500.00 now\!\!'     I need $500.00 now!! 3   % echo 'This is going to be a long line so     Unmatched '. 4   % echo 'This is going to be a long line so \     I used the backslash to suppress the newline'     This is going to be a long line so     I used the backslash to suppress the newline 

EXPLANATION

  1. The string is enclosed in single quotes. All characters, except the history (bang) character (!), are protected from shell interpretation.

  2. The !! must be protected from shell interpretation by using the backslash character.

  3. The quotes must be matched on the same line, or the shell reports Unmatched.

  4. If the line is to be continued, the backslash character is used to escape the newline character. The quote is matched at the end of the next line. Even though the shell ignored the newline, the echo command did not.

Double Quotes. Double quotes must be matched, will allow variable and command substitution, and hide everything else except the history (bang) character (!). The backslash will not escape the dollar sign when enclosed in double quotes.

Example 9.61
1   % set name = Bob     % echo "Hi $name"     Hi Bob 2   % echo "I don't have time."     I don't have time. 3   % echo "WOW!"       # Watch the history metacharacter!     ": Event not found. 4   % echo "Whoopie\!"     Whoopie! 5   % echo "I need \$5.00"     I need \.00 

EXPLANATION

  1. The local variable name is assigned the value Bob. The double quotes allow the dollar sign to be used for variable substitution.

  2. The single quote is protected within double quotes.

  3. Double or single quotes will not protect the exclamation point from shell interpretation. The built-in history command is looking for the last command that began with a double quote and that event was not found.

  4. The backslash is used to protect the exclamation point.

  5. The backslash does not escape the dollar sign when used within double quotes.

The Quoting Game. As long as the quoting rules are adhered to, double quotes and single quotes can be used in a variety of combinations in a single command.

Example 9.62
1   % set name = Tom 2   % echo "I can't give $name" ' $5.00\!'     I can't give Tom $5.00! 3   % echo She cried, \"Oh help me\!' "', $name.     She cried, "Oh help me!", Tom. 

EXPLANATION

  1. The local variable name is assigned Tom.

  2. The single quote in the word can't is protected when enclosed within double quotes. The shell would try to perform variable substitution if the dollar sign in $5.00 were within double quotes. Therefore, the string $5.00 is enclosed in single quotes so that the dollar sign will be a literal. The exclamation point is protected with a backslash since neither double nor single quotes can protect it from shell interpretation.

  3. The first conversational quotes are protected by the backslash. The exclamation point is also protected with a backslash. The last conversational quotes are enclosed in a set of single quotes. Single quotes will protect double quotes.

Steps to Successful Quoting. In a more complex command, it is often difficult to match quotes properly unless you follow the steps listed here. (See Appendix C.)

  1. Know the UNIX command and its syntax. Before variable substitution, hard code the values into the command line to see if you get the expected results.

    % nawk -F: '/^Zippy Pinhead/{print "Phone is " $2}' datafile 408-123-4563 
  2. If the UNIX command worked correctly, then plug in the variables. At this point, do not remove or change any quotes. Simply put the variables in place of the words they represent. In this example, replace Zippy Pinhead with $name.

    % set name = "Zippy Pinhead" % nawk -F: '/^$name/{print "Phone is " $2}' datafile 
  3. Play the quoting game as follows: Starting at the left-hand side with the first single quote, insert a matching single quote just before the dollar sign in $name. Now you have a set of matched quotes.

    nawk -F: '/^'$name/{print "Phone is " $2}' datafile 

    Now, right after the last letter, e in $name, place another single quote. (Believe me, this works.) This quote matches the quote after the closing curly brace.

    % nawk -F: '/^'$name'/{print "Phone is  " $2}' datafile 

    Count the number of single quotes, starting at the left-hand side. You have four, a nice even number. Everything within each set of single quotes is ignored by the shell. The quotes are matched as follows:

    graphics/09infig01.gif

  4. Last step: Double quote the variables. Surround each variable very snugly within a set of double quotes. The double quotes protect the whitespace in the expanded variable; for example, the space in Zippy Pinhead is protected.

    graphics/09infig02.gif

Quoting Variables. The :x and :q modifiers are used when it is necessary to quote variables.

Quoting with the :q Modifier. The :q modifier is used to replace double quotes.

Example 9.63
1   % set name = "Daniel Savage" 2   % grep $name:q database         same as 3   % grep "$name" database 4   % set food = "apple pie" 5   % set dessert = ( $food    "ice cream") 6   % echo $#dessert     3 7   % echo $dessert[1]     apple 8   % echo $dessert[2]     pie 9   % echo $dessert[3]     ice cream 10  % set dessert = ($food:q "ice cream") 11  % echo $#dessert     2 12  % echo $dessert[1]     apple pie 13  % echo $dessert[2]     ice cream 

EXPLANATION

  1. The variable is assigned the string Daniel Savage.

  2. When :q is appended to the variable, the variable is quoted. This is the same as enclosing the variable in double quotes.

  3. The double quotes surrounding the variable $name allow variable substitution to take place, but protect any whitespace characters. Without the double quotes, the grep program will search for Daniel in a file called Savage and a file called database.

  4. The variable food is assigned the string apple pie.

  5. The variable dessert is assigned an array (wordlist) consisting of apple pie and ice cream.

  6. The number of elements in the dessert array is three. When the food variable was expanded, the quotes were removed. There are three elements, apple, pie, and ice cream.

  7. The first element of the array is printed. The variable expands to separated words if not quoted.

  8. The second element of the array is printed.

  9. Since "ice cream" is quoted, it is treated as one word.

  10. The dessert array is assigned apple pie and ice cream. The :q can be used to quote the variable in the same way double quotes quote the variable; i.e., $food:q is the same as "$food".

  11. The array consists of two strings, apple pie and ice cream.

  12. The first element of the array, apple pie, is printed.

  13. The second element of the array, ice cream, is printed.

Quoting with the :x Modifier. If you are creating an array and any of the words in the list contain metacharacters, :x prevents the shell from interpreting the metacharacters when performing variable substitution

Example 9.64
1   % set  things = "*.c  a??  file[1 5]"     % echo $#things     1 2   % set newthings = ( $things )     set: No match. 3   % set newthings = ( $things:x ) 4   % echo $#newthings     3 5   % echo "$newthings[1] $newthings[2] $newthings[3] "     *.c  a??   file[1-5] 6   %  grep $newthings[2]:q filex            The question marks in a?? would be used for filename expansion     it is not quoted 

EXPLANATION

  1. The variable things is assigned a string. Each string contains a wildcard. The number of elements in the variable is one, one string.

  2. When attempting to create an array out of the string things, the C shell tries to expand the wildcard characters to perform filename substitution within things and produces a No match.

  3. The :x extension prevents the shell from expanding the wildcards in the things variable.

  4. The array newthings consists of three elements.

  5. To print the elements of the array, they must be quoted or, again, the shell will try to expand the wildcards.

  6. The :q quotes the variable just as though the variable were surrounded by double quotes. The grep program will print any lines containing the pattern a?? in file filex.

9.2 Programming with the C Shell

9.2.1 Steps in Creating a Shell Script

A shell script is normally written in an editor and consists of commands interspersed with comments. Comments are preceded by a pound sign and consist of text used to document what is going on.

The First Line. At the top left corner, the line preceded by #! (often called shbang) indicates the program that will be executing the lines in the script. This line is commonly

#!/bin/csh 

The #!, also called a magic number, is used by the kernel to identify the program that should be interpreting the lines in the script. When a program is loaded into memory, the kernel will examine the first line. If the first line is binary data, the program will be executed as a compiled program; if the first line contains the #!, the kernel will look at the path following the #! and start that program as the interpreter. If the path is /bin/csh, the C shell will interpret the lines in the program. This line must be the top line of your script or the line will be treated as a comment line.

When the script starts, the .cshrc file is read first and executed, so that anything set within that file will become part of your script. You can prevent the .cshrc from being read into your script by using the f (fast) option to the C shell program. This option is written as

#!/bin/csh -f 

Comments. Comments are lines preceded by a pound sign (#). They are used to document your script. It is sometimes difficult to understand what the script is supposed to do if it is not commented. Although comments are important, they are often too sparse or not even used at all. Try to get used to commenting what you are doing not only for someone else, but also for yourself. Two days from now you may not remember exactly what you were trying to do.

Making the Script Executable. When you create a file, it is not given execute permission. You need this permission to run your script. Use the chmod command to turn on execute permission.

Example 9.65
1   % chmod +x myscript 2   % ls -lF  myscript     -rwxr--xr--x    1  ellie  0 Jul  13:00 myscript* 

EXPLANATION

  1. The chmod command is used to turn on execute permission for the user, the group, and others.

  2. The output of the ls command indicates that all users have execute permission on the myscript file. The asterisk at the end of the filename (resulting from the -F option) also indicates that this is an executable program.

An Example Scripting Session. In the following example, the user will create the script in the editor. After saving the file, the execute permissions are turned on with the chmod command, and the script is executed. If there are errors in the program, the C shell will respond immediately.

Example 9.66
(The Script - info)     #!/bin/csh -f     # This script is called info 1   echo Hello ${LOGNAME}! 2   echo The hour is 'date +%H' 3   echo "This machine is 'uname -n'" 4   echo The calendar for this month is 5   cal 6   echo The processes you are running are: 7   ps -ef | grep  "^ *$LOGNAME" 8   echo "Thanks for coming. See you soon\!\!"     (The Command Line)     % chmod +x info     % info 1   Hello ellie! 2   The hour is 09 3   This machine is jody 4   The calendar for this month is 5       July 2001     S    M   Tu   W   Th   F   S                        1   2   3     4    5    6   7    8   9  10    11   12   13  14   15  16  17    18   19   20  21   22  23  24    25   26   27  28   29  30  31 7   The processes you are running are:     < output of ps prints here > 8   Thanks for coming. See you soon!! 

EXPLANATION

1. The user is greeted. The variable LOGNAME holds the user's name. On BSD systems, USER is used. The curly braces shield the variable from the exclamation point. The exclamation point does not need to be escaped because it will not be interpreted as a history character unless there is a character appended to it.

2. The date command is enclosed in backquotes. The shell will perform command substitution and the date's output, the current hour, will be substituted into the echo string.

3. The uname n command displays the machine name.

4. The cal command is not enclosed in backquotes because when the shell performs command substitution, the newlines are all stripped from the output. This produces a strange-looking calendar. By putting the cal command on a line by itself, the formatting is preserved.

5. The calendar for this month is printed.

6,7. The user's processes are printed. Use ps aux for BSD.

8. The string is printed. Note that the two exclamation points are prepended with backslashes. This is necessary to prevent history substitution.

9.2.2 Reading User Input

The $< Variable. To make a script interactive, a special C shell variable is used to read standard input into a variable. The $< symbol reads a line from standard input up to but not including the newline, and assigns the line to a variable[6].

Example 9.67
(The Script - greeting)    #/bin/csh  -f    # The greeting script 1  echo -n  "What is your name? " 2  set name = $< 3  echo Greetings to you, $name. (The Command Line)    % chmod +x greeting    % greeting 1  What is your name?  Dan Savage 3  Greetings to you, Dan Savage. 

EXPLANATION

  1. The string is echoed to the screen. The n option causes the echo command to suppress the newline at the end of the string. On some versions of echo, use a \c at the end of the string to suppress the newline; e.g., echo hello\c.

  2. Whatever is typed at the terminal, up to a newline is stored as a string in the name variable.

  3. The string is printed after variable substitution is performed.

Creating a Wordlist from the Input String. Since the input from the $< variable is stored as a string, you may want to break the string into a wordlist.

Example 9.68
1   % echo What is your full name\? 2   % set name = $<     Lola Justin Lue 3   % echo Hi $name[1]     Hi Lola Justin Lue 4   % echo $name[2]     Subscript out of range. 5   % set name = ( $name ) 6   % echo Hi $name[1]     Hi Lola 7   % echo $name[2] $name[3]     Justin Lue 

EXPLANATION

  1. The user is asked for input.

  2. The special variable $< accepts input from the user in a string format.

  3. Since the value Lola Justin Lue is stored as a single string, the subscript [1] displays the whole string. Subscripts start at one.

  4. The string consists of one word. There are not two words, so by using a subscript of [2], the shell complains that the Subscript is out of range.

  5. To create a wordlist, the string is enclosed in parentheses. An array is created. The string is broken up into a list of words and assigned to the variable name.

  6. The first element of the array is printed.

  7. The second and third elements of the array are printed.

9.2.3 Arithmetic

There is not really a need to do math problems in a shell script, but sometimes arithmetic is necessary, e.g., to increment or decrement a loop counter. The C shell supports integer arithmetic only. The @ symbol is used to assign the results of calculations to numeric variables.

Arithmetic Operators. The following operators in Table 9.8 are used to perform integer arithmetic operations. They are the same operators as found in the C programming language. See Table 9.13 for operator precedence. Also borrowed from the C language are shortcut assignment operators, shown in Table 9.9.

Table 9.8. Operators
Function Operator
Addition +
Subtraction -
Division /
Multiplication *
Modulus %
Left shift <<
Right shift >>

 

Table 9.9. Shortcut Operations
Operator Example Equivalent to
+= @ num += 2 @ num = $num + 2
-= @ num -= 4 @ num = $num - 4
*= @ num *= 3 @ num = $num * 3
/= @ num /= 2 @ num = $num / 2
++ @ num++ @ num = $num + 1
- - @ num- - @ num = $num - 1
Example 9.69
1   % @ sum = 4 + 6     echo $sum     10 2   % @ sum++     echo $sum     11 3   % @ sum += 3     echo $sum     14 4   % @ sum--     echo $sum     13 5   % @ n = 3+4     @: Badly formed number 

EXPLANATION

  1. The variable sum is assigned the result of adding 4 and 6. (The space after the @ is required.)

  2. The variable sum is incremented by 1.

  3. The variable sum is incremented by 3.

  4. The variable sum is decremented by 1.

  5. Spaces are required after the @ symbol and surrounding the operator.

Floating Point Arithmetic. Since floating point arithmetic is not supported by this shell, if you should need more complex mathematical operations, you can use UNIX utilities.

The bc and nawk utilities are useful if you need to perform complex calculations.

Example 9.70
(The Command Line) 1   set n='echo "scale=3; 13 / 2" | bc'     echo $n     6.500 2   set product='nawk -v x=2.45 -v y=3.124 'BEGIN{\     printf "%.2f\n", x * y }''     % echo $product     7.65 

EXPLANATION

  1. The output of the echo command is piped to the bc program. The scale is set to 3; that is, the number of significant digits to the right of the decimal point that will be printed. The calculation is to divide 13 by 2. The entire pipeline is enclosed in backquotes. Command substitution will be performed and the output assigned to the variable n.

  2. The nawk program gets its values from the argument list passed in at the command line. Each argument passed to nawk is preceded by the v switch; for example, v x=2.45 and v y=3.124. After the numbers are multiplied, the printf function formats and prints the result with a precision of 2 places to the right of the decimal point. The output is assigned to the variable product.

9.2.4 Debugging Scripts

C shell scripts often fail because of some simple syntax error or logic error. Options to the csh command are provided to help you debug your programs. See Table 9.10.

Table 9.10. Echo ( x) and Verbose ( v)
As options to csh
csh x scriptname Display each line of script after variable substitution and before execution.
csh v scriptname Display each line of script before execution, just as you typed it.
csh n scriptname Interpret but do not execute commands.
As arguments to the set command
set echo Display each line of script after variable substitution and before execution.
set verbose Display each line of script before execution, just as you typed it.
As the first line in a script
#!/bin/csh xv Turns on both echo and verbose. These options can be invoked separately or combined with other csh invocation arguments.
Example 9.71
(The -v and -x Options) 1   % cat practice     #!/bin/csh     echo Hello $LOGNAME     echo The date is 'date'     echo Your home shell is $SHELL     echo Good-bye $LOGNAME 2   % csh -v practice     echo Hello $LOGNAME     Hello ellie     echo The date is 'date'     The date is Sun May 23 12:24:07 PDT  2001     echo Your login shell is $SHELL     Your login shell is /bin/csh     echo Good-bye $LOGNAME     Good-bye ellie 3   % csh -x practice     echo Hello ellie     Hello ellie     echo The date is 'date'     date     The date is Sun May 23 12:24:15 PDT  2001     echo Your login shell is /bin/csh     Your login shell is /bin/csh     echo Good-bye ellie     Good-bye ellie 

EXPLANATION

  1. The contents of the C shell script are displayed. Variable and command substitution lines are included so that you can see how echo and verbose differ.

  2. The v option to the csh command causes the verbose feature to be enabled. Each line of the script is displayed as it was typed in the script, and then the line is executed.

  3. The x option to the csh command enables echoing. Each line of the script is displayed after variable and command substitution are performed, and then the line is executed. Since this feature allows you to examine what is being replaced as a result of command and variable substitution, it is used more often than the verbose option.

Example 9.72
(Echo and Verbose) 1   % cat practice     #!/bin/csh     echo Hello $LOGNAME     echo The date is 'date'     set echo     echo Your home shell is $SHELL     unset echo     echo Good-bye $LOGNAME     % chmod +x practice 2   % practice     Hello ellie     The date is Sun May 26 12:25:16 PDT  2001 --> echo Your login shell is /bin/csh --> Your login shell is /bin/csh --> unset echo     Good-bye ellie 

EXPLANATION

  1. The echo option is set and unset within the script. This enables you to debug certain sections of your script where you have run into a bottleneck, rather than echoing each line of the entire script.

  2. The > marks where the echoing was turned on. Each line is printed after variable and command substitution and then executed.

Example 9.73
1   % cat practice     #!/bin/csh     echo Hello $LOGNAME     echo The date is 'date'     set verbose     echo Your home shell is $SHELL     unset verbose     echo Good-bye $LOGNAME 2   % practice     Hello ellie     The date is Sun May 23 12:30:09 PDT  2001 --> echo Your login shell is $SHELL --> Your login shell is /bin/csh --> unset verbose     Good-bye ellie 

EXPLANATION

  1. The verbose option is set and unset within the script.

  2. The > marks where verbose was turned on. The lines are printed just as they were typed in the script and then executed.

9.2.5 Command Line Arguments

Shell scripts can take command line arguments. Arguments are used to modify the behavior of the program in some way. The C shell assigns command line arguments to positional parameters and enforces no specific limit on the number of arguments that can be assigned (the Bourne shell sets a limit of nine positional parameters). Positional parameters are number variables. The scriptname is assigned to $0, and any words following the scriptname are assigned to $1, $2, $3 ${10}, ${11}, and so on. $1 is the first command line argument. In addition to using positional parameters, the C shell provides the argv built-in array.

Positional Parameters and argv. If using the argv array notation, a valid subscript must be provided to correspond to the argument being passed in from the command line or the error message Subscript out of range is sent by the C shell. The argv array does not include the scriptname. The first argument is $argv[1], and the number of arguments is represented by $#argv. (There is no other way to represent the number of arguments.) See Table 9.11 for a list of command line arguments.

Table 9.11. Command Line Arguments
Argument Meaning
$0 The name of the script.
$1, $2, ${10} The first and second positional parameters are referenced by the number preceded by a dollar sign. The curly braces shield the number 10 so that it does not print the first positional parameter followed by a zero.
$* All the positional parameters.
$argv[0] Not valid; nothing is printed. C shell array subscripts start at 1.
$argv[1] $argv[2] The first argument, second argument, etc.
$argv[*] All arguments.
$argv All arguments.
$#argv The number of arguments.
$argv[$#argv] The last argument.
Example 9.74
(The Script) #!/bin/csh  f # The greetings script # This script greets a user whose name is typed in at the # command line. 1   echo $0 to you $1 $2 $3 2   echo Welcome to this day 'date | awk '{print $1, $2, $3}'' 3   echo Hope you have a nice day, $argv[1]\! 4   echo Good bye $argv[1] $argv[2] $argv[3] (The Command Line)     % chmod +x greetings     % greetings Guy Quigley 1   greetings to you Guy Quigley 2   Welcome to this day Fri Aug 28 3   Hope you have a nice day, Guy! 4   Subscript out of range 

EXPLANATION

  1. The name of the script and the first three positional parameters are to be displayed. Since there are only two positional parameters coming in from the command line, Guy and Quigley, $1 becomes Guy, $2 becomes Quigley, and $3 is not defined.

  2. The awk command is quoted with single quotes so that the shell does not confuse awk's field numbers $1, $2, and $3 with positional parameters. (Do not confuse awk's field designators $1, $2, and $3 with the shell's positional parameters.)

  3. The argv array is assigned values coming in from the command line. Guy is assigned to argv[1] and its value is displayed. You can use the argv array to represent the command line arguments within your script, or you can use positional parameters. The difference is that positional parameters do not produce an error if you reference one that has no value, whereas an unassigned argv value causes the script to exit with the Subscript out of range error message.

  4. The shell prints the error Subscript out of range because there is no value for argv[3].

9.2.6 Flow Control and Conditional Constructs

When making decisions, the if, if/else, if/else if, and switch commands are used. These commands control the flow of the program by allowing decision-making based on whether an expression is true or false.

Testing Expressions. An expression consists of a set of operands separated by operators. Operators and precedence are listed in Tables 9.12 and 9.13. To test an expression, the expression is surrounded by parentheses. The C shell evaluates the expression, resulting in either a zero or nonzero numeric value. If the result is nonzero, the expression is true; if the result is zero, the expression is false.

Table 9.12. Comparison and Logical Operators
Operator Meaning Example
== Is equal to $x == $y
!= Is not equal to $x != $y
> Is greater than $x > $y
>= Is greater than or equal to $x >= $y
< Is less than $x < $y
<= Is less than or equal to $x <= $y
=~ String matches $ans =~ [Yy]*
!~ String does not match $ans !~ [Yy]*
! Logical not ! $x
|| Logical or $x || $y
&& Logical and $x && $y

When evaluating an expression with the logical and (&&), the shell evaluates from left to right. If the first expression (before the &&) is false, the shell assigns false as the result of the entire expression, never checking the remaining expressions. If the first expression is false, the whole expression is false when using the logical and (&&) operator. Both expressions surrounding a logical && operator must be true for the entire expression to evaluate to true.

When evaluating an expression with the logical or (||), if the first expression to the left of the || is true, the shell assigns true to the entire expression and never checks further. In a logical || expression, only one of the expressions must be true.

The logical not is a unary operator; that is, it evaluates one expression. If the expression to the right of the not operator is true, the expression becomes false. If it is false, the expression becomes true.

Precedence and Associativity. Like C, the C shell uses precedence and associativity rules when testing expressions. If you have an expression with a mix of different operators, such as the following:

@  x = 5 + 3 * 2 echo $x 11 

the shell reads the operators in a certain order. Precedence refers to the order of importance of the operator. Associativity refers to whether the shell reads the expression from left to right or right to left when the precedence is equal.[7] Other than in arithmetic expressions (which you will not readily need in shell scripts anyway), the order of associativity is from left to right if the precedence is equal. You can change the order by using parentheses. (See Table 9.13.)

@ x = ( 5 + 3 ) * 2 echo $x 16 

Expressions can be numeric, relational, or logical. Numeric expressions use the following arithmetic operators:

+   * /  ++ -- % 

Relational expressions use the operators that yield either a true (nonzero) or false (zero) result:

>  <   >=   <=   ==   != 

Logical expressions use these operators:

!   &&  || 
Table 9.13. Operator Precedence
Precedence Operator Meaning
High ( ) Change precedence; group
  ~ Complement
  ! Logical not, negation
  * / % Multiply, divide, modulo
  + - Add, subtract
  << >> Bitwise left and right shift
  > >= < <= Relational operators: greater than, less than
  == != Equality: equal to, not equal to
  =~ !~ Pattern matching: matches, does not match
  & Bitwise and
  ^ Bitwise exclusive or
  | Bitwise inclusive or
  && Logical and
Low || Logical or

The if Statement. The simplest form of conditional is the if statement. After the if is tested, and if the expression evaluates to true, the commands after the then keyword are executed until the endif is reached. The endif keyword terminates the block. The if statement may be nested as long as every single if statement is terminated with a matching endif. The endif goes with the nearest enclosing if.

FORMAT

if ( expression ) then    command    command endif 
Example 9.75
(In the Script: Checking for Arguments) 1   if ( $#argv != 1 ) then 2       echo "$0 requires an argument" 3       exit 1 4   endif 

EXPLANATION

  1. This line reads: If the number of arguments ($#argv) passed in from the command line is not equal to one, then

  2. If the first line is true, this line and line 3 are executed.

  3. The program exits with a value of one, meaning it failed.

  4. Every if block is closed with an endif statement.

Testing and Unset or Null Variables. The $? special variable is used to test if a variable has been set. It will return true if the variable is set to null.

Example 9.76
(From .cshrc File) if ( $?prompt ) then    set history = 32 endif 

EXPLANATION

The .cshrc file is executed every time you start a new csh program. $? is used to check to see if a variable has been set. In this example, the shell checks to see if the prompt has been set. If the prompt is set, you are running an interactive shell, not a script. The prompt is only set for interactive use. Since the history mechanism is only useful when running interactively, the shell will not set history if you are running a script.

Example 9.77
(The Script)     echo -n "What is your name? " 1   set name = $< 2   if ( "$name" != "" ) then        grep "$name" datafile     endif 

EXPLANATION

  1. The user is asked for input. If the user just presses Enter, the variable name is set, but it is set to null.

  2. The variable is double quoted so that if the user enters more than one word in name, the expression will still be evaluated. If the quotes were removed and the user entered first and last name, the shell would exit the script with the error message if: Expression syntax. The empty double quotes represent a null string.

The if/else Statements. The if/else construct is a two-way branching control structure. If the expression after the if command is true, the block following it is executed; otherwise, the block after the else is executed. The endif matches the innermost if statement and terminates the statement.

FORMAT

if ( expression ) then     command else     command endif 
Example 9.78
1   if ( $answer =~ [Yy]* ) then 2      mail bob < message 3   else 4      mail john < datafile 5   endif 

EXPLANATION

  1. This line reads: If the value of $answer matches a Y or a y, followed by zero or more characters, then go to line 2; otherwise, go to line 3. (The * is a shell metacharacter.)

  2. The user bob is mailed the contents of the file message.

  3. The commands under the else are executed if line 1 is not true.

  4. The user john is mailed the contents of the file datafile.

  5. The endif block ends the if block.

Debugging Expressions. The x option (called echoing) to the C shell allows you to trace what is going on in your script as it executes. If you are unsure what is going on, this is a good way to debug your script.

Example 9.79
(The Script: Using Logical Expressions and Checking Values)     #!/bin/csh -f     # Scriptname: logical     set x = 1     set y = 2     set z = 3 1   if ( ( "$x" && "$y" ) ||  ! "$z" ) then       # Note: grouping and parentheses 2     echo TRUE    else       echo FALSE    endif (The Output) 3   % csh -x logical     set x = 1     set y = 2     set z = 3     if  ( ( 1 &&  2 )  ||  !  3 )  then     echo TRUE     TRUE     else     % 

EXPLANATION

  1. The logical expression is being evaluated. The first expression is enclosed in parentheses (not necessary since && is of higher precedence than ||). The parentheses do not require spaces when nested, but the negation operator (!) must have a space after it.

  2. If the expression evaluates true, this line is executed.

  3. The csh program is executed with the x switch. This turns on echoing. Every line in your script is echoed back to you after variable substitution has been performed.

The if Statement and a Single Command. If an expression is followed by a single command, the then and endif keywords are not necessary.

FORMAT

if ( expression ) single command 
Example 9.80
if ($#argv == 0) exit 1 

EXPLANATION

The expression is tested. If the number of command line arguments, $#argv, is equal to zero, the program is exited with a status of one.

The if/else if Statements. The if/else if construct offers a multiway decision-making mechanism. A number of expressions can be tested, and when one of the expressions evaluated is true, the block of statements that follow is executed. If none of the expressions are true, the else block is executed.

FORMAT

if ( expression ) then     command     command else if ( expression ) then     command     command else     command endif 
Example 9.81
(The Script: grade)     #!/bin/csh -f     # This script is called grade     echo -n "What was your grade? "     set grade = $< 1   if ( $grade >= 90 && $grade <= 100 ) then        echo "You got an A\!" 2   else if ( $grade > 79 ) then        echo "You got a B" 3   else if ( $grade > 69 ) then        echo "You're average"     else 4      echo "Better study" 5   endif 

EXPLANATION

  1. If grade is greater than or equal to 90 and grade is less than or equal to 100, then You got an A! is printed. Both expressions surrounding the && must be true or program control will go to the else if on line 2.

  2. If line 1 is false, test the expression (line 2), and if it is true, You got a B is printed.

  3. If line 1 and 2 are both false, try this one. If this expression is true, then echo You're average is printed.

  4. If all of the above expressions test false, the statements in the else block are executed.

  5. The endif ends the entire if construct.

Exit Status and the Status Variable. Every UNIX command returns an exit status. If the command was successful, it returns an exit status of zero. If the command failed, it returns a nonzero exit status. You can test to see whether the command succeeded or failed by looking at the value of the C shell status variable. The status variable contains the exit status of the last command executed.

Example 9.82
1   % grep ellie /etc/passwd     ellie:pHAZk66gA:9496:41:Ellie:/home/jody/ellie:/bin/csh 2   % echo $status     0                  # Zero shows that grep was a success 3   % grep joe /etc/passwd 4   % echo $status     1                  # Nonzero shows that grep failed 

EXPLANATION

  1. The grep program found ellie in the /etc/passwd file.

  2. The grep program, if it finds the pattern ellie, returns a zero status when it exits.

  3. The grep program did not find joe in the /etc/passwd file.

  4. The grep program returns a nonzero status if the pattern is not found.

Exiting from a Shell Script. In your shell script, the exit command will take you back to the shell prompt. The exit command takes an integer value to indicate the type of exit. A nonzero argument indicates failure; zero indicates success. The number must be between 0 and 255.

Example 9.83

graphics/09prfig01.gif

EXPLANATION

  1. If the number of arguments passed in from the command line ($#argv) is not equal to one, then go to line 2.

  2. The echo prints the scriptname ($0) and the string requires an argument.

  3. The program exits back to the prompt with a value of 2. This value will be stored in the status variable of the parent shell.

  4. The end of the conditional if.

  5. At the command line, the program checkon is executed without an argument.

  6. The program exits with a value of 2, which is stored in the status variable.

Using the Status Variable in a Script. The status variable can be used in a script to test the status of a command. The status variable is assigned the value of the last command that was executed.

Example 9.84
(The Script)     #!/bin/csh -f 1   ypmatch  $1  passwd >& /dev/null 2   if ( $status == 0 ) then 3        echo Found $1 in the NIS database     endif 

EXPLANATION

  1. The ypmatch program checks the NIS database to see if the name of the user, passed in as the first argument, is in the database.

  2. If the status returned from the last command is zero, the then block is executed.

  3. This line is executed if the if test expression evaluated to be true.

Evaluating Commands within Conditionals. The C shell evaluates expressions in conditionals. To evaluate commands in conditionals, curly braces must enclose the command. If the command is successful, that is, returns an exit status of zero, the curly braces tell the shell to evaluate the expression as true (1).[8] If the command fails, the exit status is nonzero, and the expression is evaluated as false (0).

It is important, when using a command in a conditional, to know the exit status of that command. For example, the grep program returns an exit status of zero when it finds the pattern it is searching for, one when it cannot find the pattern, and two when it cannot find the file. When awk or sed are searching for patterns, those programs return zero whether or not they are successful in the pattern search. The criterion for success with awk and sed is based on whether or not the syntax is right; that is, if you typed the command correctly, the exit status of awk and sed is zero.

If the exclamation mark is placed before the expression, it nots the entire expression so that if true, it is now false, and vice versa. Make sure a space follows the exclamation mark, or the C shell will invoke the history mechanism.

FORMAT

if  { ( command ) }  then     command     command endif 
Example 9.85
    #!/bin/csh -f 1   if  { ( who | grep $1 >& /dev/null ) } then 2      echo $1 is logged on and running: 3      ps -ef | grep "^ *$1"       # ps  aux for BSD 4   endif 

EXPLANATION

  1. The who command is piped to the grep command. All of the output is sent to /dev/null, the UNIX "bit bucket." The output of the who command is sent to grep; grep searches for the name of the user stored in the $1 variable (first command line argument). If grep is successful and finds the user, an exit status of zero is returned. The shell will then invert the exit status of the grep command to yield one, or true. If the shell evaluates the expression to be true, it executes the commands between the then and endif.

  2. If the C shell evaluates the expression in line 1 to be true, lines 2 and 3 are executed.

  3. All the processes running and owned by $1 are displayed.

  4. The endif ends the if statements.

 

FORMAT

if ! { (command) } then 
Example 9.86
1   if  !  { ( ypmatch $user passwd >& /dev/null ) } then 2      echo $user is not a user here.        exit 1 3   endif 

EXPLANATION

  1. The ypmatch command is used to search the NIS passwd file, if you are using a network. If the command succeeds in finding the user ($user) in the passwd file, the expression evaluates to be true. The exclamation point (!) preceding the expression nots or complements the expression; that is, makes it false if it is true, and vice versa.

  2. If the expression is not true, the user is not found and this line is executed.

  3. The endif ends this if block.

The goto. A goto allows you to jump to some label in the program and start execution at that point. Although gotos are frowned upon by many programmers, they are sometimes useful for breaking out of nested loops.

Example 9.87
(The Script)     #!/bin/csh -f 1   startover: 2   echo "What was your grade? "     set grade = $< 3   if ( "$grade" < 0 || "$grade" > 100 ) then 4      echo "Illegal grade" 5      goto startover     endif     if ( $grade >= 89 ) then        echo "A for the genius\!"     else if ( $grade >= 79 ) then        .. < Program continues > 

EXPLANATION

  1. The label is a user-defined word with a colon appended. The label is called startover. During execution of the program, the label is ignored by the shell, unless the shell is explicitly directed to go to the label.

  2. The user is asked for input.

  3. If the expression is true, (the user entered a grade less than 0 or greater than 100), the string Illegal grade is printed, and the goto starts execution at the named label, startover. The program continues to execute from that point.

  4. The if expression tested false, so this line is printed.

  5. The goto sends control to line 1 and execution starts after the label, startover.

File Testing. The C shell has a built-in set of options for testing attributes of files, such as, Is it a directory, a plain file (not a directory), or a readable file, and so forth. For other types of file tests, the UNIX test command is used. The built-in options for file inquiry are listed in Table 9.14.

Table 9.14. File Testing
Test Flag (What It Tests) True If
r Current user can read the file.
w Current user can write to the file.
x Current user can execute the file.
e File exists.
o Current user owns the file.
z File is zero length.
d File is a directory.
f File is a plain file.
Example 9.88
    #!/bin/csh -f 1   if (  e file ) then        echo file exists     endif 2   if (  d file ) then         echo file is a directory     endif 3   if ( !  z file ) then         echo file is not of zero length     endif 4   if ( -r file && -w file ) then         echo file is readable and writeable     endif 

EXPLANATION

  1. The statement reads, if the file exists, then

  2. The statement reads, if the file is a directory, then

  3. The statement reads, if the file is not of zero length, then

  4. The statement reads, if the file is readable and writeable, then . The file testing flags cannot be stacked, as in rwx file. A single option precedes the filename (e.g., r file && w file && x file).

The test Command and File Testing. The UNIX test command includes options that were built-in to the C shell, as well as a number of options that were not. See Table 9.15 for a list of test options. You may need these additional options when testing less common attributes of files such as block and special character files, or setuid files. The test command evaluates an expression and returns an exit status of either zero for success or one for failure. When using the test command in an if conditional statement, curly braces must surround the command so that the shell can evaluate the exit status properly.[9]

To use the test command in a conditional statement, use curly braces as you would for any other command for the C shell to evaluate the exit status properly.

Table 9.15. File Testing with the test Command
Option Meaning Tests True If
b File is a block special file.
c File is a character special file.
d File exists and is a directory file.
f File exists and is a plain file.
g File has the set-group-ID bit set.
k File has the sticky bit set.
p File is a named pipe.
r Current user can read the file.
s File exists and is not empty.
t n n is file descriptor for terminal.
u File has the set-user-ID bit set.
w Current user can write to the file.
x Current user can execute the file.
Example 9.89
1   if { test   b file } echo file is a block device file 2   if { test  u file }  echo file has the set user id bit set 

EXPLANATION

  1. The statement reads, if the file is a block special file (found in /dev), then

  2. The statement reads, if the file is a setuid program (set user ID), then

Nesting Conditionals. Conditional statements can be nested. Every if must have a corresponding endif (else if does not have an endif). It is a good idea to indent nested statements and line up the ifs and endifs so that you can read and test the program more effectively.

Example 9.90

graphics/09prfig02.gif

EXPLANATION

  1. If file (after variable substitution) is a file that does not exist (note the not operator, !), the commands under the then keyword are executed. An exit value of one means that the program failed.

  2. If the file is a directory, print testing is a directory.

  3. If the file is not a directory, else if the file is a plain file, then the next statement is executed, another if.

  4. This if is nested in the previous if. If file is readable, writeable, and executable, then . This if has its own endif and is lined up to indicate where it belongs.

  5. The endif terminates the innermost if construct.

  6. The endif terminates the outermost if construct.

The switch Command. The switch command is an alternative to using the if then else if construct. Sometimes the switch command makes a program clearer to read when handling multiple options. The value in the switch expression is matched against the expressions, called labels, following the case keyword. The case labels will accept constant expressions and wildcards. The label is terminated with a colon. The default label is optional, but its action is taken if none of the other cases match the switch expression. The breaksw is used to transfer execution to the endsw. If a breaksw is omitted and a label is matched, any statements below the matched label are executed until either a breaksw or endsw is reached.

FORMAT

switch (variable) case constant:     commands     breaksw case constant:     commands     breaksw endsw 
Example 9.91
(The Script - colors)     #!/bin/csh -f     # This script is called colors 1   echo -n "Which color do you like? " 2   set color = $< 3   switch ("$color") 4   case bl*:        echo I feel $color        echo The sky is $color 5      breaksw 6   case red:          # Is is red or is it yellow? 7   case yellow: 8      echo The sun is sometimes $color. 9      breaksw 10  default: 11     echo $color not one of the categories. 12     breaksw 13  endsw (The Command Line) % colors (The Output) 1   Which color do you like? red 8   The sun is sometimes red. 1   Which color do you like? Doesn't matter 11  Doesn't matter is not one of the categories. 

EXPLANATION

  1. The user is asked for input.

  2. The input is assigned to the color variable.

  3. The switch statement evaluates the variable. The variable is enclosed in double quotes in case the user entered more than one word. The switch statement evaluates a single word or string of words if the string of words is held together with double quotes.

  4. The case label is bl*, meaning that the switch expression will be matched against any set of characters starting with b, followed by an l. If the user entered blue, black, blah, blast, etc., the commands under this case label would be executed.

  5. The breaksw transfers program control to the endsw statement.

  6. If the switch statement matches this label, red, the program starts executing statements until the breaksw on line 9 is reached. Line 8 will be executed. The sun is sometimes red is displayed.

  7. If line 4 is not matched, cases red and yellow are tested.

  8. If either label, red or yellow, is matched, this line is executed.

  9. The breaksw transfers program control to the endsw statement.

  10. The default label is reached if none of the case labels matches the switch expression. This is like the if/else if/else construct.

  11. This line is printed if the user enters something not matched in any of the above cases.

  12. This breaksw is optional since the switch will end here. It is recommended to leave the breaksw here so that if more cases are added later, it will not be overlooked.

  13. The endsw terminates the switch statement.

Nesting Switches. Switches can be nested; i.e., a switch statement and its cases can be contained within another switch statement as one of its cases. There must be an endsw to terminate each switch statement. A default case is not required.

Example 9.92
(The Script: systype)     #!/bin/csh -f     # This script is called systype     # Program to determine the type of system you are on.     echo "Your system type is: " 1   set release = ('uname -r') 2   switch ('uname -s') 3   case SunOS: 4      switch ("$release") 5      case 4.*:               echo "SunOS $release"               breaksw 6      case [56].*:               echo "Solaris $release"               breaksw 7      endsw        breaksw     case HP*:        echo HP-UX        breaksw     case Linux:        echo Linux        breaksw 8   endsw (The Command Line) % systype Your system type: SunOS 4.1.2 

EXPLANATION

  1. The variable release is assigned the output of uname -r, the release number for the version of the operating system.

  2. The switch command evaluates the output of uname -s, the name of the operating system.

  3. If the system type is SunOS, the case command on line 3 is executed.

  4. The value of the variable release is evaluated in each of the cases for a match.

  5. The case for all release versions 4 are tested.

  6. The case for all release versions 5 and 6 are tested.

  7. The inner switch statement is terminated.

  8. The outer switch statement is terminated.

9.2.7 Loops

Looping constructs allow you to execute the same statements a number of times. The C shell supports two types of loops: the foreach loop and the while loop. The foreach loop is used when you need to execute commands on a list of items, one item at a time, such as a list of files or a list of usernames. The while loop is used when you want to keep executing a command until a certain condition is met.

The foreach Loop. The foreach command is followed by a variable and a wordlist enclosed in parentheses. The first time the loop is entered, the first word in the list is assigned to the variable. The list is shifted to the left by one and the body of the loop is entered. Each command in the loop body is executed until the end statement is reached. Control returns to the top of the loop. The next word on the list is assigned to the variable, the commands after the foreach line are executed, the end is reached, control returns to the top of the foreach loop, the next word in the wordlist is processed, and so on. When the wordlist is empty, the loop ends.

FORMAT

foreach variable (wordlist)        commands end 
Example 9.93
1   foreach person (bob sam sue fred) 2      mail $person < letter 3   end 

EXPLANATION

  1. The foreach command is followed by a variable, person, and a wordlist enclosed in parentheses. The variable person will be assigned the value bob the first time the foreach loop is entered. Once bob has been assigned to person, bob is shifted off (to the left) and sam is at the beginning of the list. When the end statement is reached, control starts at the top of the loop, and sam is assigned to the variable person. This procedure continues until fred is shifted off, at which time the list is empty and the loop is over.

  2. The user bob will be mailed the contents of the file letter the first time through the loop.

  3. When the end statement is reached, loop control is returned to the foreach, and the next element in the list is assigned to the variable person.

Example 9.94
(The Command Line) % cat maillist tom dick harry dan (The Script - mailtomaillist)     #!/bin/csh -f     # This script is called mailtomaillist 1   foreach person ('cat maillist') 2      mail $person <<EOF        Hi $person,        How are you?  I've missed you. Come on over        to my place.        Your pal,               $LOGNAME@'uname  n'     EOF 3   end 

EXPLANATION

  1. Command substitution is performed within the parentheses. The contents of the file maillist become the wordlist. Each name in the wordlist (tom, dick, harry, dan) is assigned, in turn, to the variable person. After the looping statements are executed and the end is reached, control returns to the foreach, a name is shifted off from the list, and assigned to the variable person. The next name in the list replaces the one that was just shifted off. The list therefore decreases in size by one. This process continues until all the names have been shifted off and the list is empty.

  2. The here document is used. Input is sent to the mail program from the first EOF to the terminating EOF. (It is important that the last EOF is against the left-hand margin and has no surrounding whitespace.) Each person in the list will be sent the mail message.

  3. The end statement for the foreach loop marks the end of the block of lines that is executed within this loop. Control returns to the top of the loop.

Example 9.95
1   foreach file (*.c) 2      cc $file -o $file:r     end 

EXPLANATION

  1. The wordlist for the foreach command is a list of files in the current directory ending in .c (i.e., all the C source files).

  2. Each file in the list will be compiled. If, for example, the first file to be processed is program.c, the shell will expand the cc command line to

    cc program.c -o program 
  3. The :r causes the .c extension to be removed.

Example 9.96
(The Command Line) 1   % runit f1 f2 f3 dir2 dir3 (The Script)     #!/bin/csh -f     # This script is called runit.     # It loops through a list of files passed as     # arguments. 2   foreach arg  ($*) 3      if (  e $arg ) then       ...          Program code continues here        else        ...         Program code continues here        endif 4   end 5   echo "Program continues here" 

EXPLANATION

  1. The scriptname is runit; the command line arguments are f1, f2, f3, dir2, and dir3.

  2. The $* variable evaluates to a list of all the arguments (positional parameters) passed in at the command line. The foreach command processes, in turn, each of the words in the wordlist, f1, f2, f3, dir2, and dir3. Each time through the loop, the first word in the list is assigned to the variable arg. After a word is assigned, it is shifted off (to the left) and the next word is assigned to arg, until the list is empty.

  3. The commands in this block are executed for each item in the list until the end statement is reached.

  4. The end statement terminates the loop after the wordlist is empty.

  5. After the loop ends, the program continues to run.

The while Loop. The while loop evaluates an expression, and as long as the expression is true (nonzero), the commands below the while command will be executed until the end statement is reached. Control will then return to the while expression, the expression will be evaluated, and if still true, the commands will be executed again, and so on. When the while expression is false, the loop ends and control starts after the end statement.

Example 9.97
(The Script)     #!/bin/csh -f 1   set num = 0 2   while ($num < 10) 3      echo $num 4      @ num++        # See arithmetic. 5   end 6   echo "Program continues here" 

EXPLANATION

  1. The variable num is set to an initial value of zero.

  2. The while loop is entered and the expression is tested. If the value of num is less than 10, the expression is true, and lines 3 and 4 are executed.

  3. The value of num is displayed each time through the loop.

  4. The value of the variable num is incremented. If this statement were omitted, the loop would continue forever.

  5. The end statement terminates the block of executable statements. When this line is reached, control is returned to the top of the while loop and the expression is evaluated again. This continues until the while expression is false (i.e., when $num is 10).

  6. Program execution continues here after the loop terminates.

Example 9.98
(The Script)     #!/bin/csh -f 1   echo  n  "Who wrote \"War and Peace\"?" 2   set answer = $< 3   while ("$answer" != "Tolstoy")        echo "Wrong, try again\!" 4      set answer = $< 5   end 6   echo Yeah! 

EXPLANATION

  1. The user is asked for input.

  2. The variable answer is assigned whatever the user inputs.

  3. The while command evaluates the expression. If the value of $answer is not equal to the string Tolstoy exactly, the message Wrong, try again! is printed and the program waits for user input.

  4. The variable answer is assigned the new input. This line is important. If the value of the variable answer never changes, the loop expression will never become false, thus causing the loop to spin infinitely.

  5. The end statement terminates the block of code inside the while loop.

  6. If the user enters Tolstoy, the loop expression tests false, and control goes to this line. Yeah! is printed.

The repeat Command. The repeat command takes two arguments, a number and a command. The command is executed that number of times.

Example 9.99
% repeat 3 echo hello hello hello hello 

EXPLANATION

The echo command is executed three times.

9.2.8 Looping Commands

The shift Command. The shift command, without an array name as its argument, shifts the argv array by one word from the left, thereby decreasing the size of the argv array by one. Once shifted off, the array element is lost.

Example 9.100
(The Script)     #!/bin/csh -f     # Script is called loop.args 1   while ($#argv) 2      echo $argv 3      shift 4   end (The Command Line) 5   % loop.args a b c d e     a b c d e     b c d e     c d e     d e     e 

EXPLANATION

  1. $#argv evaluates to the number of command line arguments. If there are five command line arguments, a, b, c, d, and e, the value of $#argv is 5 the first time in the loop. The expression is tested and yields 5, true.

  2. The command line arguments are printed.

  3. The argv array is shifted one to the left. There are only four arguments left, starting with b.

  4. The end of the loop is reached, and control goes back to the top of the loop. The expression is reevaluated. This time, $#argv is 4. The arguments are printed, and the array is shifted again. This goes on until all of the arguments are shifted off. At that time, when the expression is evaluated, it will be 0, which is false, and the loop exits.

  5. The arguments a, b, c, d, and e are passed to the script via the argv array.

The break Command. The break command is used to break out of a loop so that control starts after the end statement. It breaks out of the innermost loop. Execution continues after the end statement of the loop.

Example 9.101

graphics/09prfig03.gif

EXPLANATION

  1. The user is asked for input.

  2. The input from the user is assigned to the variable answer (answer: Mickey Mantle).

  3. The while expression reads: While the value of answer does not begin with a big M or little m, followed by zero or more of any character, enter the loop.

  4. The user gets to try again. The variable is reset.

  5. If the variable answer matches M or m, break out of the loop. Go to the end statement and start executing statements at line 7.

  6. The end statement terminates this block of statements after the loop.

  7. After the loop exits, control starts here and this line is executed.

Example 9.102
    #!/bin/csh -f     # This script is called database 1   while (1)        echo "Select a menu item" 2      cat << EOF        1) Append        2) Delete        3) Update        4) Exit     EOF 3      set choice = $< 4      switch ($choice)        case 1:               echo "Appending" 5             break           # Break out of loop; not a breaksw        case 2:               echo "Deleting"               break        case 3:               echo "Updating"               break        case 4:               exit 0        default: 6             echo "Invalid choice. Try again.       endsw 7   end 8   echo "Program continues here" 

EXPLANATION

  1. This is called an infinite loop. The expression always evaluates to one, which is true.

  2. This is a here document. A menu is printed to the screen.

  3. The user selects a menu item.

  4. The switch command evaluates the variable.

  5. If the user selects a valid choice, between 1 and 4, the command after the appropriate matching case label is executed. The break statement causes the program to break out of the loop and start execution on line 8. Don't confuse this with the breaksw statement, which merely exits the switch at endsw.

  6. If the default case is matched, that is, none of the cases are matched, program control goes to the end of the loop and then starts again at the top of the while. Since the expression after the while always evaluates true, the body of the loop is entered and the menu is displayed again.

  7. End of the while loop statements.

  8. After the loop is exited, this line is executed.

Nested Loops and the repeat Command. Rather than a goto, the repeat command can be used to break out of nested loops. The repeat command will not do this with the continue command.

Example 9.103

graphics/09prfig04.gif

(The Output) Hello, in 1st loop In 2nd loop In 3rd loop Out of all loops 

EXPLANATION

  1. Start the first while loop.

  2. Enter the second nested while loop.

  3. Enter the third nested while loop.

  4. The repeat command will cause break to be executed three times; it will break first out of this innermost loop, then the second loop, and last, the first loop. Control continues at line 5.

  5. Program control starts here after loop terminates.

The continue Command. The continue statement starts execution at the top of the innermost loop.

Example 9.104

graphics/09prfig05.gif

EXPLANATION

  1. The variable done is assigned zero.

  2. The expression is tested. It reads: while (! 0). Not 0 is evaluated as true (logical not).

  3. If the user entered No, no, or nope (anything starting with N or n), the expression is true and the continue statement returns control to the top of the loop where the expression is reevaluated.

  4. If answer does not start with N or n, the variable done is reset to one. When the end of the loop is reached, control starts at the top of the loop and the expression is tested. It reads: while (! 1). Not 1 is false. The loop exits.

  5. This marks the end of the while loop.

Example 9.105

graphics/09prfig06.gif

EXPLANATION

  1. A file check is done. If the file memo does not exist, the user is sent an error message and the program exits with a status of 1.

  2. The loop will assign each person in the list to the variable person, in turn, and then shift off the name in the list to process the next one.

  3. If the person's name is Karl or karl, the continue statement starts execution at the top of the foreach loop (Karl is not sent the memo because his name was shifted off after being assigned to person). The next name in the list is assigned to person.

  4. Everyone on the mailing list is sent the memo, except karl.

9.2.9 Interrupt Handling

If a script is interrupted with the Interrupt key, it terminates and control is returned to the C shell, that is, you get your prompt back. The onintr command is used to process interrupts within a script. It allows you to ignore the interrupt (^C) or transfer control to another part of the program before exiting. Normally, the interrupt command is used with a label to "clean up" before exiting. The onintr command without arguments restores the default action.

Example 9.106
(The Script) 1   onintr  finish 2      < Script continues here > 3   finish: 4   onintr          # Disable further interrupts 5   echo Cleaning temp files 6   rm $$tmp* ; exit 1 

EXPLANATION

  1. The onintr command is followed by a label name. The label finish is a user-defined label; control will be transferred to the finish label if an interrupt occurs. Usually this line is at the beginning of the script. It is not in effect until it is executed in the script.

  2. The rest of the script lines are executed unless ^C (Interrupt key) is pressed while the program is in execution, at which time, control is transferred to the label.

  3. This is the label; when the interrupt comes in, the program will continue to run, executing the statements below the label.

  4. To shield this part of the script from interrupts, the onintr is used. If Control-C is entered now, it will be ignored.

  5. This line is echoed to the screen.

  6. All tmp files are removed. The tmp files are prefixed with the shell's PID ($$) number and suffixed with any number of characters. The program exits with a status of 1.

9.2.10 setuid Scripts

Whoever runs a setuid program temporarily (as long as he or she is running the setuid program) becomes the owner of that program and has the same permissions as the owner. The passwd program is a good example of a setuid program. When you change your password, you temporarily become root, but only during the execution of the passwd program. That is why you are able to change your password in the /etc/passwd (or /etc/shadow) file, which normally is off-limits to regular users.

Shell programs can be written as setuid programs. You might want to do this if you have a script that is accessing a file containing information that should not be accessible to regular users, such as salary or personal information. If the script is a setuid script, the person running the script can have access to the data, but it is still restricted from others. A setuid program requires the following steps:

  1. In the script, the first line is

        #!/bin/csh  feb The  feb options:      f   fast start up; don't execute .cshrc      e  abort immediately if interrupted      b  this is a setuid script 
  2. Next, change the permissions on the script so that it can run as a setuid program:

    % chmod 4755 script_name             or % chmod +srx script_name % ls  l  rwsr xr x   2 ellie         512 Oct 10 17:18 script_name 

9.2.11 Storing Scripts

After creating successful scripts, it is customary to collect them in a common script directory and change your path so that the scripts can be executed from any location.

Example 9.107
1   % mkdir ~/bin 2   % mv myscript ~/bin 3   % vi .login     In .login reset the path to add ~/bin. 4      set path = ( /usr/ucb /usr /usr/etc ~/bin . ) 5   (At command line)     % source .login 

EXPLANATION

  1. Make a directory under your home directory called bin, or any other name you choose.

  2. Move any error-free scripts into the bin directory. Putting buggy scripts here will just cause problems.

  3. Go into your .login file and reset the path.

  4. The new path contains the directory ~/bin, which is where the shell will look for executable programs. Since it is near the end of the path, a system program that may have the same name as one of your scripts will be executed first.

  5. By sourcing the .login, the path changes are affected; it is not necessary to log out and back in again.

9.2.12 Built-In Commands

Rather than residing on disk like UNIX commands, built-in commands are part of the C shell's internal code and are executed from within the shell. If a built-in command occurs as any component of a pipeline except the last, it is executed in a subshell. See Table 9.16 for a list of built-in commands.

Table 9.16. Built-In Commands and Their Meanings
Built-In Command Meaning
: Interpret null command, but perform no action.
alias A nickname for a command.
bg [%job] Run the current or specified jobs in the background.
break Break out of the innermost foreach or while loop.
breaksw Break from a switch, resuming after the endsw.
case label: A label in a switch statement.
cd [dir] chdir [dir] Change the shell's working directory to dir. If no argument is given, change to the home directory of the user.
continue Continue execution of the nearest enclosing while or foreach.
default: Label the default case in a switch statement. The default should come after all case labels.
dirs [ l] Print the directory stack, most recent to the left; the first directory shown is the current directory. With the l argument, produce an unabbreviated printout; use of the ~ notation is suppressed.
echo [ n] list Write the words in list to the shell's standard output, separated by space characters. The output is terminated with a newline unless the n option is used.
eval command Run command as standard input to the shell and execute the resulting commands. This is usually used to execute commands generated as the result of command or variable substitution, since parsing occurs before these substitutions (e.g., eval 'tset -s options').
exec command Execute command in place of the current shell, which terminates.
exit [(expr)] Exit the shell, either with the value of the status variable or with the value specified by expr.
fg [% job] Bring the current or specified job into the foreground.
foreach var (wordlist) See "The foreach Loop" on page 412.
glob wordlist Perform filename expansion on wordlist. Like echo, but no escapes (\) are recognized. Words are delimited by null characters in the output.
goto label See "The goto" on page 405.
hashstat Print a statistics line indicating how effective the internal hash table has been at locating commands (and avoiding execs). An exec is attempted for each component of the path where the hash function indicates a possible hit, and in each component that does not begin with a backslash.
history [ hr] [n] Display the history list; if n is given, display only the n most recent events.
r Reverse the order of the printout to be most recent first rather than oldest first.
h Display the history list without leading numbers. This is used to produce files suitable for sourcing using the h option to source.
if (expr) See "Flow Control and Conditional Constructs".
else if (expr2) then See "Flow Control and Conditional Constructs".
jobs [ l] List the active jobs under job control.
l List IDs in addition to the normal information.
kill [ sig] [pid] [%job] kill l Send the TERM (terminate) signal, by default or by the signal specified, to the specified ID, the job indicated, or the current job. Signals are given either by number or name. There is no default. Typing kill does not send a signal to the current job. If the signal being sent is TERM (terminate) or HUP (hangup), then the job or process is sent a CONT (continue) signal as well.
l List the signal names that can be sent.
limit [ h] [resource max use]] Limit the consumption by the current process or any process it spawns, each not to exceed max use on the specified resource. If max use is omitted, print the current limit; if resource is omitted, display all limits.
h Use hard limits instead of the current limits. Hard limits impose a ceiling on the values of the current limits. Only the superuser may raise the hard limits. Resource is one of: cputime, maximum CPU seconds per process; filesize, largest single file allowed; datasize, maximum data size (including stack) for the process; stacksize, maximum stack size for the process; coredump, maximum size of a core dump; and descriptors, maximum value for a file descriptor.
login [username| p] Terminate a login shell and invoke login(1). The .logout file is not processed. If username is omitted, login prompts for the name of a user.
p Preserve the current environment (variables).
logout Terminate a login shell.
nice [+n| n] command] Increment the process priority value for the shell or command by n. The higher the priority value, the lower the priority of a process and the slower it runs. If command is omitted, nice increments the value for the current shell. If no increment is specified, nice sets the nice value to 4. The range of nice values is from 20 through 19. Values of n outside this range set the value to the lower or higher boundary, respectively.
+n Increment the process priority value by n.
n Decrement by n. This argument can be used only by the superuser.
nohup [command] Run command with HUPs (hangups) ignored. With no arguments, ignore HUPs throughout the remainder of a script.
notify [%job] Notify the user asynchronously when the status of the current or of a specified job changes.
onintr [ | label] Control the action of the shell on interrupts. With no arguments, onintr restores the default action of the shell on interrupts. (The shell terminates shell scripts and returns to the terminal command input level.) With the minus sign argument, the shell ignores all interrupts. With a label argument, the shell executes a goto label when an interrupt is received or a child process terminates because it was interrupted.
popd [+n] Pop the directory stack and cd to the new top directory. The elements of the directory stack are numbered from zero, starting at the top.
+n Discard the nth entry in the stack.
pushd [+n | dir] Push a directory onto the directory stack. With no arguments, exchange the top two elements.
+n Rotate the nth entry to the top of the stack and cd to it.
dir Push the current working directory onto the stack and change to dir.
rehash Recompute the internal hash table of the contents of directories listed in the path variable to account for new commands added.
repeat count command Repeat command count times.
set [var [= value]] See "Variables".
setenv [VAR [word]] See "Variables". The most commonly used environment variables, USER, TERM, and PATH, are automatically imported to and exported from the csh variables, user, term, and path; there is no need to use setenv for these. In addition, the shell sets the PWD environment variable from the csh variable cwd whenever the latter changes.
shift [variable] The components of argv, or variable, if supplied, are shifted to the left, discarding the first component. It is an error for variable not to be set, or to have a null value.
source [ h] name Read commands from name. Source commands may be nested, but if they are nested too deeply, the shell may run out of file descriptors. An error in a sourced file at any level terminates all nested source commands. Used commonly to reexecute the .login or .cshrc files to ensure variable settings are handled within the current shell, i.e., shell does not create a child shell (fork). Place commands from the filename on the history list without executing them.
h
stop [%job] Stop the current or specified background job.
suspend Stop the shell in its tracks, much as if it had been sent a stop signal with ^Z. This is most often used to stop shells started by su.
switch (string) See "The switch Command" on page 409.
time [command] With no argument, print a summary of time used by this C shell and its children. With an optional command, execute command and print a summary of the time it uses.
umask [value] Display the file creation mask. With value, set the file creation mask. Value, given in octal, is xored with the permissions of 666 for files and 777 for directories to arrive at the permissions for new files. Permissions cannot be added via umask.
unalias pattern Discard aliases that match (filename substitution) pattern. All aliases are removed by unalias.*
unhash Disable the internal hash table.
unlimit [ h] [resource] Remove a limitation on resource. If no resource is specified, all resource limitations are removed. See the description of the limit command for the list of resource names.
h Remove corresponding hard limits. Only the superuser may do this.
unset pattern Remove variables whose names match (filename substitution) pattern. All variables are removed by 'unset *'; this has noticeably distasteful side effects.
unsetenv variable Remove variable from the environment. Pattern matching, as with unset, is not performed.
wait Wait for background jobs to finish (or for an interrupt) before prompting.
while (expr) See "The while Loop" on page 415.
%[job] [&] Bring the current or indicated job to the foreground. With the ampersand, continue running job in the background.
@ [var =expr] @ [var[n] =expr] With no arguments, display the values for all shell variables. With arguments, the variable var, or the nth word in the value of var, is set to the value that expr evaluates to.

C SHELL LAB EXERCISES

Lab 19: Getting Started

1:

What does the init process do?

2:

What is the function of the login process?

3:

How do you know what shell you are using?

4:

How can you change your login shell?

5:

Explain the difference between the .cshrc and .login files. Which one is executed first?

6:

Edit your .cshrc file as follows:

  1. Create three of your own aliases.

  2. Reset your prompt.

Set the following variables and put a comment after each variable explaining what it does:

noclobber    # Protects clobbering files from redirection overwriting history ignoreeof savehist filec
7:

Type the following:

source .cshrc

What does the source command do?

8:

Edit your .login file as follows:

  1. Welcome the user.

  2. Add your home directory to the path if it is not there.

  3. Source the login file.

9:

Type history. What is the output?

  1. How do you reexecute the last command?

  2. Now type: echo a b c

Use the history command to reexecute the echo command with only its last argument, c.

Lab 20: Shell Metacharacters

1:

Type at the prompt:

touch ab abc a1 a2 a3 all a12 ba ba.1 ba.2 filex filey AbC ABC ABc2 abc
2:

Write and test the command that will do the following:

  1. List all files starting with a.

  2. List all files ending in at least one digit.

  3. List all files starting with an a or A.

  4. List all files ending in a period, followed by a digit.

  5. List all files containing just two of the letter a.

  6. List three character files where all letters are uppercase.

  7. List files ending in 11 or 12.

  8. List files ending in x or y.

  9. List all files ending in a digit, an uppercase letter, or a lowercase letter.

  10. List all files containing a b.

  11. Remove two character files starting with a.

Lab 21: Redirection

1:

What are the names of the three file streams associated with your terminal?

2:

What is a file descriptor?

3:

What command would you use to do the following:

  1. Redirect the output of the ls command to a file called lsfile?

  2. Redirect and append the output of the date command to lsfile?

  3. Redirect the output of the who command to lsfile? What happened?

4:

What happens when you type cp all by itself?

  1. How do you save the error message from the above example to a file?

5:

Use the find command to find all files, starting from the parent directory, and of type directory. Save the standard output in a file called found and any errors in a file called found.errs.

6:

What is noclobber? How do you override it?

7:

Take the output of three commands and redirect the output to a file called gotemail.

8:

Use a pipe(s) with the ps and wc commands to find out how many processes you are currently running.

Lab 22: First Script

1:

Write a script called greetme that will do the following:

  1. Greet the user.

  2. Print the date and time.

  3. Print a calendar for this month.

  4. Print the name of your machine.

  5. Print a list of all files in your parent directory.

  6. Print all the processes you are running.

  7. Print the value of the TERM, PATH, and HOME variables.

  8. Print Please couldn't you loan me $50.00?

  9. Tell the user Good bye and the current hour. (See man pages for the date command.)

2:

Make sure your script is executable.

chmod +x greetme
3:

What was the first line of your script?

Lab 23: Getting User Input

1:

Write a script called nosy that will do the following:

  1. Ask the user's full name first, last, and middle name.

  2. Greet the user by his or her first name.

  3. Ask the user's year of birth and calculate the user's age.

  4. Ask the user's login name and print user's ID (from /etc/passwd).

  5. Tell the user his or her home directory.

  6. Show the user the processes he or she is running.

  7. Tell the user the day of the week, and the current time in nonmilitary time.

The output should resemble the following:

The day of the week is Tuesday and the current time is 04:07:38 PM.

2:

Create a text file called datafile (unless this file has already been provided for you.)

Each entry consists of fields separated by colons. The fields are:

  1. First and last name

  2. Phone number

  3. Address

  4. Birth date

  5. Salary

3:

Create a script called lookup that will do the following:

  1. Contain a comment section with the scriptname, your name, the date, and the reason for writing this script. The reason for writing this script is to display the datafile in sorted order.

  2. Sort the datafile by last names.

  3. Show the user the contents of the datafile.

  4. Tell the user the number of entries in the file.

4:

Try the echo and verbose commands for debugging your script. How did you use these commands?

Lab 24: Command Line Arguments

1:

Write a script called rename that will do the following:

  1. Take two filenames as command line arguments, the first file is the old file and the second file is the new one.

  2. Rename the old filename with the new filename.

  3. List the files in the directory to show the change.

2:

Write a script called checking that will do the following:

  1. Take a command line argument, a user's login name.

  2. Test to see if a command line argument was provided.

  3. Check to see if the user is in the /etc/passwd file. If so, print the following:

    Found <user> in the /etc/passwd file.

    Otherwise, print the following:

    No such user on our system.

Lab 25: Conditionals and File Testing

1:

In the lookup script, ask the user if he or she would like to add an entry to the datafile. If yes or y:

  1. Prompt the user for a new name, phone, address, birth date, and salary. Each item will be stored in a separate variable. You will provide the colons between the fields and append the information to the datafile.

  2. Sort the file by last names. Tell the user you added the entry, and show the line preceded by the line number.

2:

Rewrite checking.

  1. After checking whether the named user is in the /etc/passwd file, the program will check to see if he or she is logged on. If so, the program will print all the processes that are running; otherwise it will tell the user the following:

    <user> is not logged on."

3:

The lookup script depends on the datafile in order to run. In the lookup script, check to see if the datafile exists and if it is readable and writeable.

4:

Add a menu to the lookup script to resemble the following:

  1. Add entry

  2. Delete entry

  3. View entry

  4. Exit

5:

You already have the Add entry part of the script written. The Add entry routine should now include code that will check to see if the name is already in the datafile and if it is, tell the user so. If the name is not there, add the new entry.

6:

Now write the code for the Delete entry, View entry, and Exit functions.

7:

The Delete part of the script should first check to see if the entry exists before trying to remove it. If the entry does not exist, notify the user; otherwise, remove the entry and tell the user you removed it. On exit, make sure that you use a digit to represent the appropriate exit status.

8:

How do you check the exit status from the command line?

Lab 26: The switch Statement

1:

Rewrite the following script using a switch statement.

#!/bin/csh  f # Grades program echo  n "What was your grade on the test? " set score = $< if ( $grade >= 90 && $grade <= 100 ) then     echo You got an A\! else if ( $grade >= 80 && $grade < 89 ) then     echo You got a B. else if ( $grade >= 79 && $grade < 79 ) then     echo "You're average." else if ( $grade >= 69 && $grade < 69 ) then     echo Better study harder else     echo Better luck next time. endif 
2:

Rewrite the lookup script using switch statements for each of the menu items.

Lab 27: Loops

1:

Write a program called picnic that will mail a list of users, one at a time, an invitation to a picnic. The list of users will be in a file called friends. One of the users listed in the friends file will be Popeye.

  1. The invitation will be in another file, called invite.

  2. Use file testing to check that both files exist and are readable.

  3. A loop will be used to iterate through the list of users. When Popeye is reached, he will be skipped over (i.e., he does not get an invitation), and the next user on the list sent an invitation, and so forth.

  4. Keep a list with the names of each person who received an invitation. Do this by building an array. After everyone on the list has been sent mail, print the number of people who received mail and a list of their names.

Bonus: If you have time, you may want to customize yourinvite file so that each user receives a letter containing his or her name. For example, the message might start:

Dear John,

I hope you can make it to our picnic .

To do this your invite file may be written:

Dear XXX,

I hope you can make it to our picnic .

With sed or awk, you could then substitute XXX with the user name. (It might be tricky putting the capital letter in the user name, since user names are always lowercase.)

2:

Add a new menu item to the lookup script to resemble the following:

  1. Add entry

  2. Delete entry

  3. Change entry

  4. View entry

  5. Exit

After the user has selected a valid entry, when the function has completed, ask the user if he or she would like to see the menu again. If an invalid entry is entered, the program should print the following:

Invalid entry, try again.

The menu will be redisplayed.

3:

Create a submenu under View entry in the lookup script. The user will be asked if he would like to view specific information for a selected individual:

  1. Phone

  2. Address

  3. Birth date

  4. Salary

4:

Add the onintr command to your script using a label. When the program starts execution at the label, any temporary files will be removed, the user will be told Good bye, and the program will exit.

[1]  Do not confuse the search path variable with the cdpath variable set in the cshrc file.

[2]  The length of the command line can be 256 characters or more; it can be even higher on different versions of UNIX.

[3]  The find syntax requires a semicolon at the end of an exec statmemt. The semicolon is preceded by a backslash to prevent the shell from interpreting it.

[4]  Programs such as grep, sed, and awk have a set of metacharacters, called regular expression metacharac-ters, for pattern matching. These should not be confused with shell metacharacters.

[5]  [ESC] stands for Escape key.

[6]  Another way to read one line of input is setvariable = 'head -1'.

[7]   Associativity in arithmetic expressions is right to left in cases of equal precedence.

[8]  The command's exit status is inverted by the shell so that the expression yields a true or false result.

[9]  A common error is to name your script test. If your search path contains the UNIX test command first, itwill execute it. The test command either displays an error or nothing at all if the syntax is correct.

CONTENTS


UNIX Shells by Example
UNIX Shells by Example, 3rd Edition
ISBN: 013066538X
EAN: 2147483647
Year: 2001
Pages: 18
Authors: Ellie Quigley

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