2

I'm confused about the evaluation of backticks in bash. I've seen code like this before which makes sense:

RESULT=`whoami`

This stores the output of the whoami command in the RESULT variable and confirms that backticks evaluate to the output of the command within them. But I've also seen code like the following:

if `wget google.com -T 2 -t 1 -o /dev/null`; then 
   echo "Internet ok"
fi

This is testing for internet connectivity by trying to get the google homepage (with a -t 1 for a single try, a -T 2 for a 2 second timeout, and a redirection to /dev/null since it doesn't actually want the output rather it just wants to see if it succeeds). The code reads as if the author was expecting that the backticks would evaluate to the exit code of the wget command rather than the output.

But it actually works. It prints Internet ok if it can connect to google.com and if I change the url to some nonsense url that doesn't exist, it doesn't satisfy the if statement and prints nothing. I don't understand why this works.

Independently running the wget command with valid and invalid urls prints nothing in both cases and only differs by exit code.

My conclusion is that there is a special construct for if followed by backticks that returns the exit code rather than the output. Am I wrong?

5

No, the backticks do not have a special meaning and you even could run the wget command without them in the if statement.
if always evaluates the exit code of the command followed.

A more comprehensive overview can be found here.


EDIT

Backticks initiate a command substitution which is executed in a subshell and returns an exit code. if just checks return codes, it does not make use of the output of the command.

To compare the output of a command you could use the [ , which must be closed with ], which basically is a test. The test could be anything

  • from man test
  • a string comparison

    [ "hello" == "test" ]
    
  • an integer test

    [ 2 -eq 3 ]  
    

If the test succeeds you get an exit code 0 ( true ), otherwise not 0 ( false ) which is again evaluated by if.

So you have something in mind like follows.

if [ "`wget google.com -T 2 -t 1 -o /dev/null`" == "" ]
then 
    echo "emptY"
fi

But this wouldn't make much sense, due to redirecting the output to /dev/null you will always get true from [ "wget google.com -T 2 -t 1 -o /dev/null" == "" ].
On the other hand it also would not be useful to check if the output of the wget command does contain output, leaving out the -o /dev/null, because even in an error situation you will get output, but different return codes.

7
  • Hmm I understand how if wget ... would work but the statement here is if `wget ...` so wouldn't it first run the wget command and replace the backticks with the output of wget, making the statement if [nothing]? – Marplesoft Feb 8 '17 at 20:11
  • Further to this, you said: "If always evaluates the exit code of the command followed" - my point is that my if statement isn't followed by a command, it's following by backticks which should evaluate to a blank string, no? – Marplesoft Feb 8 '17 at 20:29
  • Appreciate your additional info but I'm afraid my core question hasn't been addressed which is less about the if and more about the backticks. X=whoami is very different from X=`whoami` so why does if wget ... do the same thing as if `wget ...` – Marplesoft Feb 8 '17 at 21:50
  • He explained it.. It works because if doesn't use the output, only the error code of subshell operation. Your example doesn't use square brackets and thus doesn't use the output of the condition command, just it's error code. Like any Unix command tje backticks subshell returns both the output and exit code. Your if example simply does not examine the output, only the error code. – Bojan Markovic Feb 9 '17 at 16:03
  • Ok so you're confirming what I was asking, that backticks when used in an if have a special and different interpretation from when used elsewhere. They don't evaluate to the output of the command but rather to the exit code. That is with if `cmd` ... means that backticks evaluate to the exit code and with x=`cmd` backticks evaluate to the output. I'm trying to clarify because this answer started off by saying that no, backticks have no special meaning when used with if. – Marplesoft Feb 9 '17 at 18:47
1

In bash if shell scripting, these days avoid backticks and use the command substitution $(...) syntax instead.

Its clearer than very vague backticks and supports nesting, something not possible in backtics. I.E:

command $(command two $(command three))
0

I finally got a clear answer to this via a discussion thread on the bash mailing list. The conclusion is as follows:

  • There is nothing special about the if statement here, it is simply executing the command after the if and then branching based on that command's exit code. The unexpected behaviour here is instead attributed to the command substitution itself.
  • A backticked command (command substitution) always evaluates to the output of the command within them, not the exit code (this isn't anything new but just confirming that whether backticks are used in an if or not is irrelevant).
  • Based on the above two points, the non-obvious reason that if `cmd` ... runs the if body based on the exit code of cmd when cmd writes nothing to stdout is that there is a POSIX spec rule about shell command execution that says:

If there is no command name, but the command contained a command substitution, the command shall complete with the exit status of the last command substitution performed. Otherwise, the command shall complete with a zero exit status.

ref: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html

  • So it's only because in the case that cmd writes nothing to stdout, the shell - rather then execute an empty command - takes the exit code of the command substitution (the exit code of cmd itself) and returns that as the exit code of the parent command - which then bubbles up to the if.

To better illustrate this see the following example:

$ `true`
$ echo $?
0
$ `false`
$ echo $?
1

The shell first runs true which emits nothing to stdout so the resulting command is empty. Where I had expected an empty command to either deterministically succeed or fail, based on the rule above, it completes with the exit code of true rather than "the exit code of an empty command".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.