Thursday, February 10, 2011

Terse vs Verbose conditional execution in Bash

As today is apparently my day for remedial Bash (see previous posts about it) I found myself with a complete script (not published) that I felt could be much more terse. This led me to start looking into ways to compress things. In the do or die post we saw one such example, a few more follow.

My examples for this post are largely from the excellent Bash by example, part 3 post on IBM. Refer to The Advanced Bash Scripting Guide sections 7.1 Test Constructs and 7.3 Other Comparison Operators for details on the comparisons used. The examples shown were tested on Solaris 5.10.

Correct number of arguments
Verbose:
if [ $# -ne 2 ]
then
  echo "Please specify ebuild file and unpack, compile or all"
  exit 1
fi
Terse:
[ $# == 2 ] || {echo "Please specify ebuild file and unpack, compile or all"; exit 1;}

This works because Bash short-circuits. That is for A || B we only execute B if A is true, if A is false the result is already known to be false and we don't have to run B at all.

NOTE: Example above was corrected to {} instead of (); this is important as you do NOT want a subshell or else exit won't actually break out of your script. { commands; } doesn't create a subshell and thus works as expected.

Set a variable to a default value if unset
Verbose:
if [ -z "$DISTDIR" ]
then
  # set DISTDIR to /usr/src/distfiles if not already set
  DISTDIR=/usr/src/distfiles
fi
Terse:
# set DISTDIR to /usr/src/distfiles if not already set
[ -z "$DISTDIR" ] && DISTDIR=/usr/src/distfiles
This time we use short-circuited &&: if DISTDIR is null the first part is true and the second part executes.

Terse option 2:
[ -n "$DISTDIR" ] || DISTDIR=/usr/src/distfiles
If DISTDIR is NOT null the first part is true and we skip the second. If DISTDIR is null the first part is false and we have to execute the second part.

Make sure we have a clean directory to work in
Verbose:
if [ -d ${WORKDIR} ]
then    
  rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
Terse:
[ ! -d ${WORKDIR} ] || rm -rf ${WORKDIR}
mkdir ${WORKDIR}
Note the addition of ! in the terse version: we want the first part to check false if the directory exists to ensure the second part executes, removing the directory.

Closing Notes
We can obviously continue with just about every if statement that only has one or two short actions.

A huge advantage of the verbose versions is that people are more used to them. Terse is nice in some ways but I strongly suspect more developers understand if [ blah ] then echo exit fi than [ something ] || (echo; exit). There may be additional ramifications to the reduction; I'm not Bash guru enough to say they are exact equivalents.

Another possible advantage to the short-form is it makes error checking easy enough to validate commands we usually just assume to work. For example, don't just tar xzf whatever and assume it unpacked, actually check it worked rather than detecting the failure later when some subsequent step failed.

Anyway, I'm far from ready to suggest that the terse form should be used everywhere but it is a useful tool to have for one-liners in the terminal and possibly to shorten scripts that have piles of dead simple if/then checks.

3 comments:

javarevisited said...

Given that we need to write bash script in vim or vi editor I would rather go for readability and to me verbose is the choice. but yes you made some point for terse version as well.

Javin
basic networking command in unix

Hamish said...

"That is for A || B we only execute B if A is true, if A is false the result is already known to be false and we don't have to run B at all."

Ah, no. If A is false, B must be evaluated, because false || true is true.

"That is for A && B we only execute B if A is true, if A is false the result is already known to be false and we don't have to run B at all."

is correct.

Dean Morin said...

{echo "Please specify ebuild file and unpack, compile or all"; exit 1;}

This is exactly what I was looking for, thank you. However, I should note a space is required on the inside of the braces.

Post a Comment