CSC 352– Unix Programming, Spring 2015 March 2015 Shell Programming (Highlights only)
Highlights of Shell Programming Chapter 13 has a lot of information. Perhaps a little too much for the first reading. We will concentrate on the main programming constructs of immediate use. These ideas carry over from programming in a language like C++. The shell makes them handier because it is an interpreter. You can interact with the shell. We will use bash.
The hash-bang line # starts a comment in the shell. #! on an executable text file says that this is an interpreter’s source file, with the path of the interpreter to follow. #!/bin/bash on the first line of a Bash script. Hash-bang works for any interpreted language with # as the comment delimiter – shells, Perl, Python, etc. Use “chmod +x FILENAME” to add execute permission.
Variables in bash (review) VARIABLE=STRING, with no spaces around the =, assigns the STRING into VARIABLE. In order to make it accessible to children processes, you must export the variable from the shell process that defined it. Use unset to remove a variable binding. C library functions getenv & putenv manipulate these. -bash-3.00$ v=1 -bash-3.00$ echo $v 1 -bash-3.00$ bash -c 'echo $v' -bash-3.00$ export v -bash-3.00$ bash -c 'echo $v' 1
Shell read and built-in variables. Use read VARIABLENAME to read the terminal into VARIABLENAME. Use echo STRING or echo –e STRING to echo to stdout; -e interprets escapes such as \n \t. Special variables: $? is exitstatus of exit exitstatus; $0 is argv[0] (program name), $1 is argv[1] (first argument), etc.; shift shifts $2 down to $1, etc.; $# is number of positional arguments; $* or give all command line arguments; set - - STRINGS sets command line arguments to whatever is in STRINGS.
Boolean expressions Any successful (0) exit status is true, non-0 is false. Shell’s test primitive tests Shell variables and files. Exit status is 0 for success. [ expression ] is shorthand for test expression. -bash-3.00$ ls *.out nohup.out -bash-3.00$ test -f nohup.out ; echo $? 0 -bash-3.00$ test -f junk.out ; echo $? 1
Shell test or [ ] expressions Numerical comparisons: -eq, -ne, -gt, -ge, -lt, -le String comparisons: s1 = s2, s1 != s2, -n stg (not null), -z stg(is null), stg (assigned and non-empty), s1 == s2 (ksh and bash only). Use “$v” or “${v}” if v may be empty; otherwise, a binary comparison operator won’t see empty string. File tests: -f, -r, -w, -x, -d, -s (size > 0), -e (exists), -L (symbolic link), f1 –nt f2 (newer than), f1 –ot f2 (older), f1 -ef f2 (linked to) for ksh and bash.
Boolean connectives && is logical AND, || is logical OR Both are short-circuited. && quits on the first failure, || on the first success. -bash-3.00$ ls *.out nohup.out -bash-3.00$ [ -f nohup.out ] && echo hello hello -bash-3.00$ [ -f junk.out ] && echo hello -bash-3.00$ [ -f nohup.out ] || echo hello -bash-3.00$ [ -f junk.out ] || echo hello hello
if control construct selects an execution path Just like you are used to in C++, but Use elif instead of else if, use fi to end an if construct. -bash-3.00$ count=`ls *out |wc -l` -bash-3.00$ echo $count 1 -bash-3.00$ if [ $count -eq 0 ] > then >echo none > elif [ $count -eq 1 ] > then > echo one > else > echo other > fi one
while iterates while a text is true while uses do and done keywords, also break and continue -bash-3.00$ set -- `date` # set command line arguments -bash-3.00$ echo "COUNT $# DATA: COUNT 6 DATA: Sun Mar 27 19:45:36 EDT bash-3.00$ i=1 -bash-3.00$ while [ $# -gt 0 ] > do > echo "var $i: $1" > i=`expr $i + 1` > shift # shift position parameters to the left by one position, discard former $1, decrement $# > done var 1: Sun var 2: Mar var 3: 27 var 4: 19:49:10 var 5: EDT var 6: 2011
for loops over a list of values -bash-3.00$ ls -d s* splitstudents startup -bash-3.00$ for f in `ls -d s*` > do > echo "file: $f" > done file: splitstudents file: startup -bash-3.00$ find ~/unix -name "s*" -print /export/home/faculty/parson/unix/seeargs /export/home/faculty/parson/unix/seeargs/seeargs.c /export/home/faculty/parson/unix/seeargs.zip -bash-3.00$ for path in `find ~/unix -name "s*" -print` > do > echo "file `basename $path` in directory `dirname $path`" > done file seeargs in directory /export/home/faculty/parson/unix file seeargs.c in directory /export/home/faculty/parson/unix/seeargs file seeargs.zip in directory /export/home/faculty/parson/unix
case similar to C++ switch-case construct case expression in pattern1) commands1 ;; pattern2) commands2 ;;... *) defaultcommands ;; esac Patterns can be numeric values, regular expressions.
Here document (<<) Use << to redirect input from the script itself. Make up a delimiter that is not in the data. -bash-3.00$ wc -l << DELIM > this is a > three-line > chuck of text > DELIM 3
Shell functions They take no parameters, work on variables in the shell process. Useful for repeated code. -bash-3.00$ fcounter() { > fcnt=`find. -type f -print |wc -l` > echo "$fcnt regular files in and below `pwd`" > } -bash-3.00$ fcounter 1537 regular files in and below /export/home/faculty/parson/process_incoming
aliases Alias is useful for changing complex command strings into simple command aliases. It is also good for pointing to a specific path. -bash-3.00$ alias alias vi='vim'
eval and exec eval takes a string and evaluates it as a program fragment. -bash-3.00$ echo -e "Enter a command that I will read as data: \c" ; read cmd Enter a command that I will read as data: date -bash-3.00$ eval $cmd Sun Mar 27 20:25:09 EDT 2011 exec replaces the current shell with the exec’d program.