0

I cannot understand the logic of what's going on with this simple Bash script:

#!/bin/bash

Check if file is present

which ./vendor/bin/non_existent_file &> /dev/null

printf "Exited with $?\n\n"

if [[ "$?" -eq 1 ]]; then echo "Number is One" else echo "Number is not one" fi

When the file is missing (non existent), the output is this:

Exited with 1

Number is not one

When the file is present, the output is this:

Exited with 0

Number is not one

???

Things I've tried:

if [ $? == 1 ]
if [ "$?" == 1 ]
if [[ "$?" == 1 ]]
if [[ $? -eq 1 ]]
if [[ "$?" = "1" ]]
if [[ "$?" == "1" ]]

Why is the IF statement always failing?

Kusalananda
  • 333,661

2 Answers2

3
which ./vendor/bin/non_existent_file &> /dev/null

This will run which and set $? to its exit status. (I'll assume for now that which works for you.)

printf "Exited with $?\n\n"

This will run printf, and set $? to its exit status.

if [[ "$?" -eq 1 ]]; then

So what gets tested is the exit status of printf.

You need to save the exit status to a temporary variable to avoid that, e.g.

which ./vendor/bin/non_existent_file &> /dev/null
ret=$?
printf 'which exited with status %d\n' "$ret"
if [[ $ret -ne 0 ]]; then
    printf "that was a falsy exit status"
fi

Though which as I know would search PATH for the named executable, but if you have a fixed path you're looking at, you could probably use [[ -x ./path ]] directly to see if the file there is executable. If you're looking for a program in PATH, you may want to see Why not use "which"? What to use then? for caveats and corner cases.

ilkkachu
  • 138,973
1

Thanks to the comments by @Kusalananda and @SottoVoce it becomes clear:

$? stores the result of the previous command, which includes printf.

So running printf "Exited with $?\n\n" changes $? to 0 by virtue of it having executed successfully.

It would be better to store the result of the execution in another variable to prevent this sort of confusion from happening.