Besides being a command interpreter, a shell is also a programming language which allows you to write high-level programs called shell scripts to perform specific tasks.
A shell script file, say myscript, can be executed in 2 ways :
1. Start a new shell with myscript as the argument.
For example:
% sh myscript <enter> - for a Bourne shell script % csh myscript <enter> - for a C shell script2. First set the script file to be readable and executable, and then invoke the script as a command. For example,
% chmod u+rx myscript <enter> % myscript <enter>5.1 Shell Programming
In general, a shell supports the following 2 functions:
1. Defining and dropping shell variables
2. Executing conditional and iterative constructs
There are two types of shell variables - global and local. Global variables (the environment list) can be accessed from the shell and its initiated processes or programs. On the other hand, local variables can only be accessed from the shell. For Bourne and Korn shells, global variables are defined from the local ones by using the export command. For C shell, global variables are defined and dropped by using the setenv and unsetenv commands.
When a shell is started, some useful shell variables such as path, home, term, etc. will automatically be defined.
For example, commands for setting the local variable workdir with the value /tmp are as follows:
$ workdir=/tmp <enter> - for Bourne shell % set workdir=/tmp <enter> - for C shellYou can then use the expression $workdir (a dollar sign in front of the shell variable) to retrieve its assigned value.
% cd $workdir ; pwd <enter> /tmp % _The commands for listing out the global and local variables which have been defined are:
Global Local
Bourne/Korn Shell printenv, set set C Shell printenv setShell variables which are commonly used in shell scripts are given below:-
Bourne/Korn shell C shell Purpose
$# $#argv Number of arguments $0 $0 Command name $1,$2,... $1,$2,... First argument, second $argv[n] argument, ...
$* $*, $argv[*] All arguments $@ $? $status Return code from the last command $$ $$ Process number of the current command5.1.2 Conditional and iterative constructs
The syntax of the various constructs for the different shells are highlighted in this section. For more details, please refer to the on-line manual pages for the Bourne, Korn and C shells.
1. A conditional expression can be evaluated to give a result which assumes a value of either true or false.
a. Bourne shell - test, expr, [ -r file ], [ 3 -ne 4 ], [ str1 != str2 ], [ condition ] etc., b. C shell - -r file, ( 3 == 3 ), ( str1 != str2 ), ( condition ) etc.,2. A conditional construct can be used to execute conditionally different commands according to the expression evaluation results.
a. Bourne shell - if [ -r file ], if ... then ... elif ... else ... fi, case ... esac b. C shell - if ( -r file ) then, if ... then ... else if ... else ... endif, switch ... endsw3. A looping construct can be used to execute a group of commands for a certain number of times or until certain conditions are met.
a. Bourne shell - for do ... done, xargs -l, until do ... done, while do ... done b. C shell - foreach ... end, xargs -l, repeat n command, while ... endExamples:
Bourne/Korn shell C shell1. To examine whether string comparison is case-sensitive:
test "abc" = "ABC" test "abc" = "ABC" if [ $? ] if ( $status )then then echo case-sensitive echo case-sensitive endif fi2. To check whether variable 'a' is equal to variable 'b':
if expr $a = $b if ( $a == $b ) then then echo a = b echo a = b endif fi3. To find out what commands are included in the .login file if it exists:
if [ -r .login ] if (-r .login) then then cat .login cat .login endif fi4. To examine if the variable char is 'a', 'b' or any other character:
case $char in switch ( $char ) 'a') case 'a': echo a echo a ;; breaksw 'b') case 'b': echo b echo b ;; breaksw *) default: echo not a or b echo not a or b ;; breaksw esac endsw5. To 'cat' all files with 'abc' as the first 3 characters of their
filenames in the current directory:
for file in abc* foreach file ( abc* ) do cat file cat file end done6. To echo the numbers 1 to 12 on separate lines on the screen:
mon=1 set mon=1 while [ $mon -le 12 ] while ( $mon < 12 ) do echo $mon echo $mon @ mon += 1 mon=`expr $mon + 1` end done7. To check the file type of all the files in the current directory by
using the file command.
ls -aC1 |xargs -l file
count=0 repeat 10 echo YES until [ $count -eq 10 ] do echo YES count=`expr $count + 1` done5.1.3 The eval command
The eval command can be used to execute a given string as it is a shell command.
For example:
1. The .login C shell script file kept under your home directory is executed every time when you login the system. It usually contains the commands for defining the default system settings. However, you may include other additional commands, as required.
For example, a .login file may contain the following lines:-
umask 077 set path=(/bin /usr/bin $HOME/bin $HOME)The first line sets the default access permission for your files and directory with a value which will enforce 'tight security' for all newly created files. The second line defines a secure path for the shell to search for commands or programs.
2. The .cshrc file includes those C shell commands which will be executed whenever a C shell is started.
For example, the .cshrc file may include the following command to specify that a VT100 terminal/emulation is used.
setenv TERM vt1003. The .profile file is the startup file for Bourne/Korn shell.
The following lines included in the file will give similar protection as the .login file given above.
umask 077 PATH=/bin:/usr/bin:$HOME/bin:$HOME: export PATHYou can define shell functions in Bourne and Korn shells instead of using aliases as in the C shell.
5.3 A sample Bourne Shell Script File
#!/bin/sh # A Bourne Script file for calculating the total # number of bytes of files under a directory # if [ $# -ne 1 ] ## the directory name then echo Usage is : $0 directory_name fi count=`expr 0` ## set total to 0 for file in $1/* ## for each file under 1 do if [ -f $file ] ## for a non-directoy file, then ## put attribute line into x x=`/bin/ls -l $file` if test $? -ne 0 then echo Regular file $file not included continue fi elif [ -d $file ] ## for a directoy file, then ## put attribute line into x x=`/bin/ls -ld $file` if test $? -ne 0 then echo Directory file $file not included continue fi y=`$0 $file` ## byte total within $file if test $? -eq 0 then count=`expr $y + $count` fi else echo File $file not included in the total continue fi x=`echo $x |cut -f4 -d" "` count=`expr $x + $count` ## add file size done echo $count ## return byte total5.4 The alternative C Shell Script
#!/bin/csh -f set nonomatch ## ignore empty directory if ( $#argv != 1) then ## 1 directory name echo Usage is: $0 directory_name exit 1 endif @ count = 0 ## set total to 0 foreach file ($1/*) ## for each file in $1 if ( -f $file ) then # for a non-directory, put attribute line into x set x = `/bin/ls -l $file` if ( $status ) then echo Regular file $file not included continue endif # for a directory, put attribute line into x else if ( -d $file ) then set x = `/bin/ls -ld $file` if ( $status ) then echo Directory file $file not included continue endif set y = `$0 $file` ## Store the total to y if ( ! $status ) @ count = $count + $y else echo File $file not included continue endif @ count = $count + $x[4] # add x[4] to count where x[4] is the 4th field end # return total bytes for the directory $1 echo $count5.5 Jobs
Your login process (shell) and its child processes can send messages to the same terminal, which will be called hereafter the control terminal. However, there are times when you do not have to know the messages sent from a process to the control terminal. Thus, you can explicitly disconnect the process from its control terminal temporarily or permanently. For permanently disconnected processes and the processes that started off with no control terminal (i.e. scheduled jobs submitted using the at command), they cannot re-gain access to a control terminal. Hence, the processes will remain in the background until their termination. In UNIX, such background processes are generally called jobs.
5.5.1 Foreground and Background Processes
UNIX distinguishes between foreground and background programs. This feature allows you to run several programs from the terminal at the same time. When a program is running in the foreground, all keyboard input will be sent to the program's standard input unless you have redirected it. Thus, you cannot do anything else until the program finishes. On the other hand, if a program is running in the background, it is disconnected from the keyboard. Subsequent keyboard input will reach the UNIX shell and is interpreted as a command. Therefore, you can run many programs simultaneously in the background but only one program at a time in the foreground.
A number of built-in commands are provided by the C and Korn shells for job control :
Ctrl/Z - switch current process to background or bg fg - switch a background process to foreground jobs - list all background jobs of the current logon sessions kill - kill processes by job numbers5.5.2 Creating Background Processes from Shell
To run a program in the background, type an ampersand (&) at the end of the command line.
Example : Run the program myprogram in the background.
$ myprogram & <enter> - for Bourne/Korn shell % myprogram & <enter> - for C shell [1] 11111 myprogramThe job number is printed in brackets ( [ ] ) followed by the process identification number (PID) for the command. It then prompts you for a new command. Entering the jobs command will produce a short report describing all the programs you are executing in the background. For example :
% myprogram & <enter> - for C shell [1] 11111 myprogram % jobs <enter> [1] + Running myprogramIn C and Korn shells, you can also run a program, say myscript, as a background job with the following steps :-
% myscript <enter> ^Z - Ctrl/Z pressed Stopped % jobs <enter> [1] Stopped myscript % bg <enter> [1] 12345 myscriptCtrl/Z is used to suspend a job running in the foreground. This stops the program but does not terminate it. Entering the background command, bg, lets a stopped program continue execution in the background.
5.5.3 Examining Background Processes
The jobs command can only be used to list out the background processes which are created and disconnected from the control terminal temporarily by the C shell.
To find out all of your background processes, you have to first get a list of all processes by using the ps command (with the all processes option) and then extract the list of your processes from the long list produced by using the grep command. You may have to use ps -ef instead of ps -aux for different versions of UNIX for the following examples.
For example:
% ps -aux |grep $USER |more <enter> USER PID %CPU %MEM SZ RSS TT STAT TIME COMMAND h9512345 11058 0.0 0.0 80 0 ? TW 0:00 /usr/ucb/man h9512345 19283 0.0 0.0 300 56 ? IW 0:00 mail h9512345 20000 0.0 0.2 364 124 05 S 0:00 -csh (csh) . . .The status field STAT can be (R) for Running, (T) for Stopped, (I) for Idle, (S) for Sleeping, (Z) for Killed but not yet removed, (W) for Waiting and (TW) for Stopped and Waiting. TT is the control terminal field where (?) implies a permanently disconnected/background process.
5.5.4 Switching Between Processes
In C and Korn shells you can switch between processes which are not disconnected permanently from the control terminal. To bring back a process from background to foreground, use the fg command. If you have more than one background job, follow fg with a job identifier - a percent sign (%) followed by the job number.
For example:
% myscript & <enter> [1] 11111 myscript % myprogram > result <enter> ^Z - Ctrl/Z pressed Stopped % _ % bg <enter> [2] 11122 myprogram > result % jobs <enter> [1] + Running myscript [2] - Running myprogram > result % fg %1 <enter> or % %1 <enter> myscript _The plus sign (+) in the report from jobs indicates which job will return to the foreground by default.
In a time-sharing system, you should be responsible for removing all strayed or unwanted processes which will unnecessarily tie up valuable system resources.
For example:
% tty <enter> /dev/tty05 % ps -aux |grep h9512345 |more <enter> USER PID %CPU %MEM SZ RSS TT STAT TIME COMMAND h9512345 11058 0.0 0.0 80 0 ? TW 0:00 /usr/ucb/man h9512345 19283 0.0 0.0 300 56 ? IW 0:00 mail h9512345 20000 0.0 0.2 364 124 05 S 0:00 -csh (csh) . . .In the above listing, it can be noted that the user h9512345 has logged into the HKUSUA system from terminal /dev/tty05 as indicated by the last line. Further, there are 2 stopped background processes (with PID's 11058 and 19283 as shown in the STAT column). These stopped processes can be killed by using the following command :
% kill -9 11058 19283 <enter>The -9 or -KILL is the sure-kill option which will terminate your processes specified by the PID's in the command.
You can also delete background processes by specifying their job numbers in C and Korn shells.
For example:
% jobs <enter> [1] + Running myscript [2] - Running myprogram > result % _ % kill %2 <enter> % jobs <enter> [1] + Running myscript % _The % must be included before the job number, otherwise UNIX will interpret it as a process number. If the job is still running, use the -9 option as a last resort.
% kill -9 %2 <enter>The -9 option does not give the process a chance to clean up its temporary files, so do not use it unless you need to.