Shell Scripts
CS3411 Spring 2009
Revisiting execve(2)
NA M E execve - execute p rogram SYN O P SIS # lude <un t inc is d.h> in execve (const char *i ename, char *cons a [, t fl t rgv ] char * const envp[ ) ]; DESC RIPTION execve( executes the program po ted to by fl name. ) in ie fl iename must be e ther a b i inary executab , o a le r sc ip sta t r t ring wi a l o the f th ine f orm "# in erpre [a ] . ! t ter rg " In the l te case, the i at r nterpre er must be a va id t l pa thname fo an executab wh is not i se fa scr t r le ich t l ip , wh wil be invoked as in rpreter [a fl ich l te rg] iename. ...
CS3411
Shell Scripts
A small example, cat r t, protected 0755: sc ip
#!/bin/cat -A line one line two
In keeping with the man page, invoke /bin t -A catscr t. /ca ip
% ./catscript #!/bin/cat -A$ line one$ line two$
Suppose we have file named scr pt, protected 0755, that starts i with #!/b /sh in Line that starts with # is a comment to /bin/sh. The file scr iptis then a shell script. If first line does not start with #!, it is as if the first line were #! in sh. /b /
CS3411
Shell Scripts
Command Line Arguments
Shell scripts can act like standard UNIX commands and take arguments from the command line. Arguments are passed from the command line into a shell program using the positional parameters $1 through $9. Each parameter corresponds to the position of the argument on the command line. The positional parameter $0 refers to the command name or name of the executable file containing the shell script. All the positional parameters can be referred to using the special parameter $*. This is useful when passing filenames as arguments. For example, pr tps: in
# This script converts ASCII files to PostScript # and sends them to the default (PostScript) printer mp $* | lpr
CS3411
Shell Scripts
This processes the three files given as arguments to the script/command printps:
./printps elm.txt vi.ref msg
Usually only nine command line arguments can be accessed using positional parameters. The sh f i tcommand gives access to command line arguments greater than nine by shifting each of the arguments. The second argument ($2) becomes the first ($1), the third ($3) becomes the second ($2) and so on. This gives you access to the tenth command line argument by making it the ninth. The first argument is no longer available. Successive shift commands make additional arguments available. Note that there is no unsh f command to bring back it arguments that have been shifted through $1!
CS3411
Shell Scripts
The shell script sh f_demo: it
#!/bin/sh echo "arg1=$1 shift echo "arg1=$1 shift echo "arg1=$1 shift echo "arg1=$1 arg2=$2 arg3=$3" arg2=$2 arg3=$3" arg2=$2 arg3=$3" arg2=$2 arg3=$3"
In execution:
% ./shift_demo one two three four five six seven arg1=one arg2=two arg3=three arg1=two arg2=three arg3=four arg1=three arg2=four arg3=five arg1=four arg2=five arg3=six
CS3411
Shell Scripts
${3 -defau t : l }means $3, unless $3 is unset or null, then it means defau t. l
#!/bin/sh echo "arg1=$1 arg2=$2 arg3=${3:-dummy}" % ./default_demo a b c arg1=a arg2=b arg3=c % ./default_demo a b arg1=a arg2=b arg3=dummy % ./default_demo a b "" arg1=a arg2=b arg3=dummy
${3-defau t l }means $3 unless $3 is unset (skips null check)
#!/bin/sh echo "arg1=$1 arg2=$2 arg3=${3-dummy}" % ./default_demo a b c arg1=a arg2=b arg3=c % ./default_demo a b arg1=a arg2=b arg3=dummy % ./default_demo a b "" arg1=a arg2=b arg3=
CS3411
Shell Scripts
Special Shell Variables
Some special shell variables: # is number of command line parameters ($# is its value) $ is the pid of the shell ($$ is its value) ? is decimal value returned by the last synchronously executed command ! is pid of the last background command invoked Common use of $$: temp file names:
echo shell pid is $$ > sh.$$ % ./tempfile.demo % ls sh.* sh.524 % cat sh.524 shell pid is 524
CS3411
Shell Scripts
Variables and Quoting
Double quotes suppresses meaning of all special characters except: $ ` \ ` is backquote (not ) \ is escape Single quotes suppresses meaning of all special characters except
#!/bin/sh # echo # echo "#" echo '#' VAR=string echo $VAR echo "$VAR" echo "\$VAR" echo '$VAR'
CS3411
[jmayo]$./special.sh
# # string string $VAR $VAR
Shell Scripts
Subshells
( cmds ) Combines stdout and stderr of cmds into one stream
[jmayo]$ ./newshell.sh 2 2 11
[jmayo]$ cat newshell.sh #!/bin/sh ( echo alpha; echo beta ) | wc
Launches another instance of command processor Does not fork new process
CS3411
Shell Scripts
10
[jmayo]$ cat newshell2.sh #!/bin/sh echo First pid is $$ (echo Second pid is $$ ) echo ----------------------./echoPid.sh
[jmayo@]$
cat echoPid.sh
echo "Echo pid is $$"
[jmayo]$ ./newshell2.sh First pid is 3249 Second pid is 3249 ----------------------Echo pid is 3251 [jmayo]$
CS3411
Shell Scripts
11
Variable Propagation
Variables defined in shell are not visible in the parent
[jmayo]$ cat prop.sh #!/bin/sh VAR=value echo "First value is <$VAR>" (echo "Sub value is <$VAR>"; VAR=newvalue; \ echo "Sub new value is <$VAR>") echo "Final value is <$VAR>"
[jmayo]$ ./prop.sh First value is <value> Sub value is <value> Sub new value is <newvalue> Final value is <value>
CS3411 Shell Scripts 12
Export
Export moves variable definition from shell to environment Other programs may access
[jmayo]$
bash bash-3.00$ VAR=value; ./echoVar.sh Echovar has value bash-3.00$ export VAR=value; ./echoVar.sh Echovar has value value bash-3.00$ exit [jmayo@asimov ~/codeExamples]$ setenv VAR value; ./echoVar.sh Echovar has value value [jmayo@asimov ~/codeExamples]$
CS3411
Shell Scripts
13
Reading Input
Can read from standard input into a shell script with the read command:
% cat readin.sh echo "Please enter your name:" read name echo "Hi, $name % ./readin.sh Please enter your name: phil Hi, phil
CS3411
Shell Scripts
14
If there is more than one word in the input, each word can be assigned to a different variable. Any words left over are assigned to the last named variable. For example:
% cat readin2.sh echo "Please enter your surname" echo "followed by your first name:" read name1 name2 echo "Hi, $name2 $name1 % ./readin2.sh Please enter your surname followed by your first name: Nimble jack b Hi, jack b nimble
CS3411
Shell Scripts
15
Return Value of a Command
Every Unix command returns a value on exit which the shell can interrogate. This value is held in the read-only shell variable ?. A value of 0 (zero) signifies success; anything other than 0 signifies failure. Unlike the C convention, 0 is essentially a true and non-zero is a false. Use that value to have conditional control flow in shell scripts: the i fstatement the && operator the | |operator
CS3411
Shell Scripts
16
The i fCommand
The i fstatement uses the exit status of the given command and conditionally executes the statements following. The general syntax is:
if test then commands (if condition is true) else commands (if condition is false) fi
The lexemes then, e lse and f iare shell reserved words and as such are only recognized after a newline or ; (semicolon). Make sure that you end each if construct with a f istatement.
CS3411
Shell Scripts
17
The i fstatement may be nested:
if ... then ... else if ... ... fi fi
The elfstatement can be used as shorthand for an else if i statement. For example:
if ... then ... elif ... ... fi fi
CS3411
Shell Scripts
18
An example:
% cat checkon.sh if finger | grep -s $1 > /dev/null then echo $1 is logged in else echo $1 not available fi % ./checkon.sh park park is logged in % ./checkon.sh sharon sharon not available
CS3411
Shell Scripts
19
The && Operator
You can use the && operator to execute a command and, if it is successful, execute the next command in the list. For example:
cmd1 && cmd2
The first command, cmd1, is executed and its exit status examined. Only if cmd1 succeeds is cmd2 executed. This is shorthand for:
if cmd1 then cmd2 fi
CS3411
Shell Scripts
20
To check for a Unix domain socket in the current working directory (a socket in the Unix domain, unlike the IPv4 domain, shows up as a directory entry):
% cat checkforsocket.sh #!/bin/sh # usage: checkforsocket.sh { ls -l | grep -s \^s > /dev/null ; } && echo found a socket % ./checkforsocket.sh % cd .. % ls -l SOCKET_0 srwxrwxrwx 1 kearns wheel 0 Apr 2 06:00 SOCKET_0 % ~/courses.d/315/checkforsocket.sh found a socket
CS3411
Shell Scripts
21
The | |Operator
You can use the | |operator to execute a command and, if it fails, execute the next command in the command list. For example:
cmd1 || cmd2
cmd1 is executed and its exit status examined. If cmd1 fails then cmd2 is executed. This is shorthand for:
cmd1 if test $? -ne 0 then cmd2 fi
CS3411
Shell Scripts
22
To send a message to a user using the appropriate utility:
% cat writemail.sh #!/bin/sh # usage: writemail.sh user message echo "$2" |{ write "$1" || mail "$1" ;} % ./writemail.sh jmayo hi write: jmayo logged in more than once ... writing to console
In my console window:
hi % ./writemail.sh root hi write: root not logged in
In roots inbox:
From: Jean Mayo <jmayo@mtu.edu> Date: Wed, 3 Apr 1996 17:25:05 -0500 (EST) To: root@mtu.edu hi
CS3411
Shell Scripts
23
The test Command
The shell uses a command called test to evaluate conditional expressions. Full details of this command can be found in the tes (1 t )manual page. Example:
% cat testfile.sh if test ! -f $1 then if test "$WARN" = "yes" then echo "$FILE does not exist" fi fi % ./testfile.sh fubar % ( setenv WARN yes ; ./testfile.sh fubar ) fubar does not exist
CS3411 Shell Scripts 24
The tes command can also do numeric tests: t
% cat testargs.sh #!/bin/sh # usage: testargs.sh arg1 arg2 ... if test $# -gt 2 then echo "more than 2 args" else echo "less than or equal to 2 args" fi % ./testargs.sh less than or equal to 2 % ./testargs.sh a less than or equal to 2 % ./testargs.sh a b less than or equal to 2 % ./testargs.sh a b c more than 2 args % ./testargs.sh "a b c" less than or equal to 2 CS3411
args args args
args Shell Scripts 25
Commonly-used alternative syntax for test, but with exactly the same semantics:
% cat testargs.sh #!/bin/sh # usage: testargs.sh arg1 arg2 ... if [ $# -gt 2 ] then echo "more than 2 args" else echo "less than or equal to 2 args" fi
CS3411
Shell Scripts
26