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
  • 320,670
  • 36
  • 633
  • 936
  • Somewhat related: [How to keep last exit status after test](https://unix.stackexchange.com/questions/209419/how-to-keep-last-exit-status-after-test) – steeldriver Nov 28 '22 at 14:58
  • [Why not use "which"? What to use then?](https://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then) – glenn jackman Nov 28 '22 at 15:19

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
  • 133,243
  • 15
  • 236
  • 397
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.