CMPSC 311, Introduction to Systems Programming

Shell Scripts, Part 1



References



A shell script is a plain text file that is interpreted line-by-line by a command interpreter (a shell).
Put the shell script in a file named xyz, or xyz.sh for emphasis.

Assuming the file name xyz, run the script with the command

sh xyz

Or, change the file permissions (set the executable bit)

chmod +x xyz

and run the script directly

xyz



Common shells

sh
Bourne shell, the Posix standard
csh
C shell
tcsh
Tenex C shell
ksh
Korn shell
bash
Bourne-Again SHell (GNU)

Bash is sh-compatible, with features from ksh and csh.

The examples here were run on Solaris unless stated otherwise.  The command prompt % is from tcsh, while the command prompt $ is from sh.  We usually add blank lines and highlight commands for clarity.

Example

% sh

$ ls
dir1   file1  file2

$ exit

%



The Bourne Shell (sh)

Execution of commands
Definitions
Comments
Simple-command
Command
Pipeline
List
Grouping

( list )
{ list ;}
Functions

name () { list ;}
Quoting



Command Substitution (single-back-quoted strings)

` command `
Example

% sh

$ ls
dir1   file1  file2

$ echo ls
ls

$ echo `ls`
dir1 file1 file2

$ ls | cat
dir1
file1
file2

$ echo `ls | cat`
dir1 file1 file2




Parameter Substitution
Parameters set by the shell

# The number of positional parameters in decimal.
- Flags supplied to the shell on invocation or by the set command.
? The decimal value returned by the last synchronously executed command.
$ The process number of this shell.
! The process number of the last background command invoked.

Parameters used by the shell (not the complete list)

HOME default argument (home directory) for the cd command, set at login from the password file
PATH search path for commands
CDPATH search path for the cd command
PS1 primary prompt string, by default "$ "
PS2 secondary prompt string, by default "> "

Example - prompt strings

% sh
$ PS1="Feed me! "
Feed me! PS2="FEED ME!! "
Feed me! echo one two
one two
Feed me! echo "one
FEED ME!! two"
one
two
Feed me! exit
%

Example - the difference between $* and $@ and "$*" and "$@"

% cat z.sh
echo ' ' .$*. ; printf "$# ."
for w in $*
do
  printf "%s." $w
done
printf "\n\n"

echo ' ' .$@. ; printf "$# ."
for w in $@
do
  printf "%s." $w
done
printf "\n\n"

echo ' ' ."$*". ; printf "$# ."
for w in "$*"
do
  printf "%s." $w
done
printf "\n\n"

echo ' ' ."$@". ; printf "$# ."
for w in "$@"
do
  printf "%s." $w
done
printf "\n\n"

% sh z.sh 1 2 3 ' ' 4 5 6
  .1 2 3 4 5 6.
7 .1.2.3.4.5.6.

  .1 2 3 4 5 6.
7 .1.2.3.4.5.6.

  .1 2 3   4 5 6.
7 .1.2.3.4.5.6.

  .1 2 3   4 5 6.
7 .1.2.3..4.5.6.




Filename Generation, pattern matching

* Matches any string, including the null string (empty string).
? Matches any single character.
[...] Matches any one of the enclosed characters.
A pair of characters separated by - matches any character lexically between the pair, inclusive.
If the first character following the opening [ is a !, any character not enclosed is matched.

The list of candidates for matching comes from the file names in the current directory (including subdirectory names but not including hidden names).  If there is no match, the word is left unchanged.

Example

$ ls
dir1   file1  file2

$ ls -a
.       ..      .file3  dir1    file1   file2

$ echo *
dir1 file1 file2

$ echo file*
file1 file2

$ echo file[123]
file1 file2

$ echo file[13]
file1

$ echo *[345]
*[345]

$ echo *?
dir1 file1 file2

$ echo ?
?

$ echo .*
. .. .file3




Expressions

The special command test is used to evaluate expressions for an exit status (0 acts as true, nonzero acts as false).  For convenience, these commands are equivalent:

test ...
[ ... ]

The program version of test is at /usr/bin/test .

Operators for the test command (not the complete list)



Conditionals

If-then-else

if   list
then
     list
elif list
then
     list
else
     list
fi
Example - which versions of the program should we try?

if [ `uname -s` = "SunOS" ]
then
  Versions="pr1.sun pr1.gcc"
else
  Versions=pr1.gcc
fi

for Program in $Versions
do
  make $Program
  # ... more later
done

Case selection with pattern matching

case word in
   pattern )
     list ;;
   pattern | pattern )
     list ;;
esac
Example - which versions of the program should we try? (continued)

for Program in $Versions
do
  case $Program in
    *.sun)
      echo "Testing Sun's C compiler"
      ;;
    *.gcc)
      echo "Testing the GNU C compiler"
      ;;
    *)    # the default case
      echo "No match for testing."
      exit
      ;;
  esac
done



Loops
Iterate over words in a sequence of words

for name in word ...
do
  list

done

Iterate over the positional parameters 1, 2, 3 ...

for name
do
  list

done

Examples

% cat x.sh
for foo
do
  echo $foo
done

% sh x.sh a b c d
a
b
c
d

% cat x.sh
for foo in x y z
do
  echo $foo
done

% sh x.sh a b c d
x
y
z

Example - which versions of the program should we try? (continued)

for Program in $Versions
do
  make $Program
  for TestCase in testdata/*
  do
    echo "---- $Program < $TestCase ----"
    ls -l $TestCase
    $Program < $TestCase
    echo
  done
done

While loop

while list
do
  list
done
Example - read standard input until end of file

while read input
do
  echo "input: " $input
done

Until loop

until list
do
  list
done
Example - read standard input until you find magic, with a function definition

% cat y.sh
test_for_magic () {
  for word in $*
  do
    echo "trying: " $word
    case $word in
      *magic*)
         return 0       # success
         ;;
    esac
  done
  return 1              # not success
}

until
  read input
  test_for_magic $input
do
  echo "rejected: " $input
done

echo "accepted: " $input

% sh y.sh
one two three 
trying:  one
trying:  two
trying:  three
rejected:  one two three
is this the magic word?
trying:  is
trying:  this
trying:  the
trying:  magic
accepted:  is this the magic word?

Special commands (not the complete list; more info in Part 2)



I/O Redirection

<word take standard input from file word
>word send standard output to file word, but first create or truncate the file
>>word same, but create or append to the file
<<word take standard input up to word or end-of-file; see example
digit>
affect file descriptor digit
digit<
affect file descriptor digit
<&digit see example
>&digit see example

The default standard input for a background command (one ending with &) is /dev/null, the empty file.

Example

% cat w.sh
while read input
do
  echo "found: " $input
done

% sh w.sh <<END
? q w e r t
? y
? u
? i
? o
? p
? END
found:  q w e r t
found:  y
found:  u
found:  i
found:  o
found:  p

Example, using sh interactively
$ cat w.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  fprintf(stdout, "abc %d\n", argc);
  fprintf(stderr, "xyz %d\n", argc);
  return 0;
}

$ cc w.c


$ a.out 1

abc 2
xyz 2

$ a.out 1> foo 2> bar


$ cat foo

abc 1

$ cat bar

xyz 1
Example - 2>&1 - merge standard output (file descriptor 1) and standard error (file descriptor 2)

% cat foo.c
void main(void) { printf("xyz\n"); return; }
// missing #include <stdio.h>

% cat bar.c
void main(void) { print("xyz\n"); return; }
// missing f?  undefined print?

% cat cr.sh
{ cc -v $1.c
  if [ -f a.out -a -x a.out ]
  then
    a.out
  fi
} 1>compile_and_run.out 2>&1

% sh cr.sh foo

% cat comp*
"foo.c", line 1: warning: implicit function declaration: printf
xyz

% sh cr.sh bar

% cat comp*
"bar.c", line 1: warning: implicit function declaration: print
Undefined                       first referenced
 symbol                             in file
print                               bar.o
ld: fatal: Symbol referencing errors. No output written to a.out



Last revised, 25 Feb. 2013