2

I was answering a question and assumed that

if [ $(command) ]; then ...

is always equivalent to

if command; then ...

However, I got a couple of counter examples:

$ [ $(true) ] && echo "yes" || echo "no"
no
$ if true; then echo "yes"; else echo "no"; fi
yes

And:

$ if echo "$xxxx"; then echo "yes"; else echo "no"; fi

yes
$ [ $(echo "$xxx") ] && echo "yes" || echo "no"
no

In both cases the problem lies in true and echo $xxxx (unset variable) do not return anything, so [ ] evaluates to False (by definition in man test), whereas echo $xxxx is successful so its return code is 0 and, therefore, the if condition evaluates to True.

However, I wonder: when can we assume both checks are equivalent? Is this a risky thing to assume or it is reliable in most of the cases?

Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598

2 Answers2

4

I would say that it is much rarer for them to be equivalent than not. if checks the return code of the command executed (0 being assumed success/true, anything else failure/false), and there isn't necessarily any correlation between output being generated on stdout (which is what [ "$(...)" ] looks for) and a 0 return code.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
4

The two statements are totally different. When you write

if command; then ...

you are testing the exit code of command. If it is zero, the block will be executed. On the other hand, when you write

if [ $(command) ]; then ...

you are actually testing if the output of the command, and not the exit code.

The reason the example you linked works, is because grep only prints to the standard output if it finds a match.

user000001
  • 32,226
  • 12
  • 81
  • 108
  • The `[ $(command) ]` case is worse even than that. It only works reliably if the output is one word (when unquoted) and when the output doesn't look like arguments to `[`/`test`. Compare `[ $(printf -- 'a = a') ]; echo $?` to `[ $(printf -- 'a = b') ]; echo $?` for example. – Etan Reisner May 05 '16 at 12:26