tags:

views:

104

answers:

4

A lot of sh code looks like:

$cmd
if [ $? = 0 ]; then $cmd2; fi

Instead of:

if $cmd; then $cmd2; fi

I have typically assumed that people use the former simply because they are unaware that the syntax of the latter is valid, but I'm wondering if there is another reason (although the only possibility that comes to mind is portability). Is there any reason to prefer explicitly referencing ${?}?.

+1  A: 

Because you're testing the status flag of the command, as opposed to testing the output from the command.

If a command prints to stdout, but sets a flag, you'll get get different results with the two examples you've given.

I stand corrected, after testing it. I thought I remembered running into a problem years ago on solaris (under ksh?), but I can't recreate under the machines I currently have access to except for testing :

if [ $cmd ]

Joe
My understanding is that the exit status of `$cmd` is tested, under both coding styles, and that stdout/stderr are not interfered with by either of them. Could you give an example of a command that gives different behaviour under the different coding styles, please?
Dave Hinton
Can you give an example of your second sentence? Because no - the results are the same.
Dennis Williamson
-1: This is not correct. Try this ...`$ failcmd(){ echo "this command sucks"; return 1; }; if failcmd; then echo success; else echo fail; fi`
SiegeX
The 'if [ $cmd ]' test is radically different from the question. It does not execute $cmd at all; it merely does some sort of test on its contents - the actual test depending on what the contents are.
Jonathan Leffler
+4  A: 

You have to reference $? if you actually want to use the error code for something (logging, handling expected errors, etc.). For the particular scenario that you mention, the second for is both clearer and shorter, and I see no reason not to use it.

JSBangs
+2  A: 

In my view, because a lot of people don't realize that you can test the status of the command more or less as you do. Similarly, people also do not realize that you can write:

if cmd1
   cmd2
   cmd3
then
    ...do this after executing cmd1, cmd2 and cmd3, but ...
    ...only if cmd3 exits with with status 0!
fi

You can also simplify your code (at the cost of terseness) to:

cmd1 && cmd2

(I see @shin noted this in a comment, which I've up-voted).

I also see a lot of 'Bourne' shell code that uses:

if ( ... )
then ...
fi

Sometimes that is appropriate - but most often it is stuff that a C shell programmer wrote not realizing that the notation means 'run a sub-shell' in Bourne shell and derivatives - such as Korn shell, the POSIX-compliant shells, and Bash.

Jonathan Leffler
Dave Hinton
@Dave Hinton: I tried `(set -e; false echo OK)` and `(false echo OK)` and both dutifully echoed OK. I suspect from your comment that you expected the former to be silent.
Jonathan Leffler
I stand corrected. I must misunderstand the bash manpage.
Dave Hinton
A: 

Yes many people don't know about the latter more elegant form. I've previously mentioned this in: http://www.pixelbeat.org/programming/shell%5Fscript%5Fmistakes.html

pixelbeat