[plug] script error

Craig Ringer craig at postnewspapers.com.au
Thu Jan 12 16:14:34 WST 2006


Mr E_T wrote:

> On another note - just because two files are the same size doesn't make them the same.
> do a diff -q files
> 
> if diff -q $FileA $FileB
>   then
>     echo same
>   else
>     echo different
>   fi

If all you want is "do they differ" then `cmp' may be better - it's much 
"dumber" than diff, and probably rather faster. It's also POSIX, so it 
should be available on any system you can find.

if cmp -s "$FileA" "$FileB"; then
...
fi

Note the quotes around the file paths. This is important. If the user 
specifies a file path like "my file" your script would break. You must 
consider spaces in file names at EVERY variable expansion; this is one 
of the more frustrating aspects of shell programming.

Jon Miller wrote:
 > Any idea how to append 1 file to another?  using cat file1 >> file2
 > does not work in a script.

It should; the command above will append the file named "file1" to the 
file named "file2", both being in the current working directory.

Did you make sure that you're referring to the right files? Did you 
check for spaces in expanded arguments? Did you check that you have 
permission to perform the operation? Did you actually mean "$file1" not 
file1 ?

I strongly recommend that you put:

set -e -u

at the top of your script (if you're using bash). This will cause your 
script to abort immediately on an error, thus forcing you to explicitly 
handle "expected" failures, and making it much easier to track down 
unexpected ones. It also causes the shell to treat access to a variable 
that has not been defined as an error, instead of silently subtituting 
the empty string like it does by default.

Both these changes make debugging and shell programming in general MUCH 
easier. You do, however, need to change a few things. You can't test if 
a variable is empty with:

if test "$var" -ne ""; then
fi

for example; instead, you must use:

if test "${var:-}" -ne ""; then
fi

If you're just trying to set a default if a variable isn't defined (say, 
the second argument) you might write something like:

destfilename="${2:-defaultname}"

To handle -e, you must make sure to explcitly handle expected failure 
cases. For example:

# Delete `fred' if it exists
rm fred >&/dev/null

should become (and should be anyway)

rm -f fred

Creating a directory if it doesn't exist should really be:

if ! test -d "$thedir"; then
    mkdir "$thedir"
fi

(Note that if $thedir exists, but is not a directory, we fail and abort 
the script. You can of course change your chosen behaviour as 
appropriate for your needs ... using -e just forces you to think about 
it more).

You should also become familiar with the `-x' option to the shell, which 
traces the execution of your script. For example:

#!/bin/sh
if test $# -eq 0; then
    echo "usage: $0 blah [ blah [ blah ...] ]"
    exit 1
fi
i=0
for arg in "$@"; do
    (( i++ ))
    echo "Argument $i: $arg"
done

when run normally shows:

[craig at albert ~]$ sh demo.sh fred123 "a b c d"
Argument 1: fred123
Argument 2: a b c d

and with sh -x:

[craig at albert ~]$ sh -x demo.sh fred123 "a b c d"
+ test 2 -eq 0
+ i=0
+ for arg in '"$@"'
+ ((  i++  ))
+ echo 'Argument 1: fred123'
Argument 1: fred123
+ for arg in '"$@"'
+ ((  i++  ))
+ echo 'Argument 2: a b c d'
Argument 2: a b c d

I'm sure you can see how that's useful in debugging.

--
Craig Ringer



More information about the plug mailing list