You can capture the exit value from the condition and propagate that forward:
while rmdir FOO; ss=$?; [[ $ss -eq 0 ]]
do
echo in loop
done
echo "out of loop with ?=$? but ss=$ss"
Output
rmdir: failed to remove 'FOO': No such file or directory
out of loop with ?=0 but ss=1
In this instance the exit status from rmdir FOO
has been captured in the variable ss
and is 1
. (Try replacing rmdir FOO
with ( exit 4 )
. You'll find that ss=4
.)
How does this work? Remember that the syntax is actually while list-1; do list-2; done
, and not the much more usual expectation of while command; do list; done
. The list-1
can be a sequence of semicolon-separated commands, and the documentation states that the "while
command continuously executes the list list-2
as long as the last command in the list list-1
returns an exit status of zero."
As an alternative presentation of the messy-looking while
condition, it is possible to assign a variable while inside an expression (( ... ))
, and then to use the result. This gives the harder-to-read but more compact assign-and-test structure:
while rmdir FOO; ((! (ss=$?)))
do
echo in loop
done
echo "out of loop with ?=$? but ss=$ss"
Alternatively you can use while rmdir FOO; ! (( ss=$? ))
. These work because ((1)) evaluates arithmetically to 1, which is generally associated with true, and so the exit code of that evaluation is 0 (success). On the other hand, ((0)) evaluates arithmetically to 0, which is generally associated with false, and so the exit code of that evaluation is 1 (failure). This may seem confusing, as after all both evaluations ((.))
are "successful", but this is a hack to bring the value of arithmetic expressions representing true/false in line with bash's exit codes of success/failure, and make conditional expressions like if ...; then ...; fi
, while ...; do ...; done
, etc, work correctly, whether based on exit codes or arithmetic values.