Procrastiblog

January 6, 2007

The Unix Shell: Argh

Filed under: Tech — Chris @ 6:26 pm

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"
% $cmd
string arg=""quoted"
bad arg: arg"
% eval $cmd
string 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 arg
bash: arg: command not found
% CMD= cmd arg
bash: cmd: command not found
% CMD= "cmd arg"
bash: cmd arg: command not found
% CMD="cmd arg"
% echo $CMD
cmd 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 foo
then bar
fi

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.

Advertisements

Blog at WordPress.com.

%d bloggers like this: