Suppose you’re trying to write a simple shell script. And suppose you very badly want to put a command into a shell variable before you execute it. And suppose that command very badly needs to accept double-quoted strings as arguments. Oh, the layers of indirection! How will the shell tokenize such a thing?
Let the command in question be the following homely shell script.
string-arg.sh: #! /bin/sh # Usage: string-arg.sh [-s STRING]* while test ${1+set}; do case $1 in "-s") if ! test ${2+set}; then echo "no string after -s" exit 1 else echo "string arg=\"$2\"" shift 2 continue fi esac echo "bad arg: $1" exit 1 done
Let’s see what happens.
% ./string-arg.sh -s "quoted arg"string arg="quoted arg"% cmd="./string-arg.sh -s \"quoted arg\""% echo $cmd./string-arg.sh -s "quoted arg"% $cmdstring arg=""quoted"bad arg: arg"% eval $cmdstring arg="quoted arg"
AND REMEMBER, KIDS: echo `foo` gives you the output of foo and { foo; echo $? } gives you its exit value.
if foo then bar fi
executes bar if foo succeeds (conventionally), which is equivalent to
foo && bar
in the short-circuiting parlance.
if ! foo then bar fi
executes bar if foo fails (conventionally), which is equivalent to
foo || bar
in the short-circuiting parlance.
if `foo`then bar fi
executes bar if the command output by foo exists and returns exit value 0 (if it doesn’t exist, it will just exit).
if test `foo`then bar fi
executes bar if foo doesn’t produce any output.
[UPDATE] One more thing. Setting shell variables.
% CMD=cmd argbash: arg: command not found% CMD= cmd argbash: cmd: command not found% CMD= "cmd arg"bash: cmd arg: command not found% CMD="cmd arg"% echo $CMDcmd arg
This often trips me up, because make is much more forgiving and I hack more Makefiles than shell scripts.
[UPDATE 1/8/2007] I got the if-then stuff wrong the first time. Which goes to show you how desperately I need this tutorial. In shell-world exit code 0 is “true” and exit code not-0 is “false”. This is sort of the opposite of the C convention, with the caveat that exit values and arithmetic values shouldn’t be conflated.
Another trip-up from C to the shell is that whitespace makes a bigger difference. if ! foo; then bar; fi is not the same as if !foo; then bar; fi.
if foothen barfi
is not the same as if foo then bar fi. {foo; bar;} is not the same as { foo; bar;}. And so on, world without end.




