I know how to check the status of the previously executed command using $?, and we can make that status using exit command. But for the loops in bash are always returning a
Git 2.27 (Q2 2020), offers a good illustration of the exit status in a loop, here within the context of aborting a failing test early (e.g. by exiting a loop), which is to say "return 1".
See commit 7cc112d (27 Mar 2020) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit b07c721, 28 Apr 2020)
t/README: suggest how to leave test early with failure
Helped-by: Jeff King
Over time, we added the support to our test framework to make it easy to leave a test early with failure, but it was not clearly documented in
t/README
to help developers writing new tests.
The documentation now includes:
Be careful when you loop
You may need to verify multiple things in a loop, but the following does not work correctly:
test_expect_success 'test three things' ' for i in one two three do test_something "$i" done && test_something_else '
Because the status of the loop itself is the exit status of the
test_something
in the last round, the loop does not fail when "test_something
" for "one
" or "two
" fails.
This is not what you want.Instead, you can break out of the loop immediately when you see a failure.
Because alltest_expect_*
snippets are executed inside a function, "return 1
" can be used to fail the test immediately upon a failure:test_expect_success 'test three things' ' for i in one two three do test_something "$i" || return 1 done && test_something_else '
Note that we still
&&
-chain the loop to propagate failures from earlier commands.
The status of the loop is the status of the last command that executes. You can use break to break out of the loop, but if the break is successful, then the status of the loop will be 0
. However, you can use a subshell and exit instead of breaking. In other words:
for i in foo bar; do echo $i; false; break; done; echo $? # The loop succeeds
( for i in foo bar; do echo $i; false; exit; done ); echo $? # The loop fails
You could also put the loop in a function and return a value from it.
I think what you should be asking is: How can I wait until a file or a directory (/test
) gets created by another process?
What you are doing up to now is polling with full power. Your loop will allocate up to 100% of the processing power of one core. The keyword is "polling", which is ethically wrong by the standards of computer scientists.
There are two remedies:
inotify
(see: man inotify
); advantage: no CPU load, great responsiveness, no delays, no arbitrary constants in your code; disadvantage: inotify
is a kernel API – you need some code to access it: inotify-tools
or some C/Perl/Python code. Have a look at inotify and bash!Something like this?
while true; do
case $RANDOM in *0) exit 27 ;; esac
done
Or like this?
rc=0
for file in *; do
grep fnord "$file" || rc=$?
done
exit $rc
The real question is to decide whether the exit code of the loop should be success or failure if one iteration fails. There are scenarios where one make more sense than the other, and other where it's not at all clear cut.
Use artificial exit code
I would like to submit an alternative solution which is simpler and I think more elegant:
(while true
do
if [ -f "test" ] ; then
break
fi
done
Of course of this is part of a script then you could user return 1 instead of exit 1
exit 1
)
echo "Exit status is: $?"