The for name in ...
loop relays the exit status of the last command executed.
When you break
a loop, the break
command itself has an exit status of 0 by default, which is therefore relayed by the for
, thus making your &&
test after the done
be equal to true
.
One way to work around that is as you said in your self-answer. That is a nice and clean one, and works well with any POSIX compliant shell.
But it has one drawback in that, by being a sub-shell, it doesn't fully share its Execution Environment1 with its "outer" shell. It does inherit it on beginning execution, but does not "propagate" it back to its "outer" parent on finishing execution, meaning for instance that any variable set or modified within the loop won't be seen after outside the loop.
Speaking for Bash, there is an alternative: you can avoid the sub-shell and still use break
while forcing it to fail by passing it a -1
argument, because -1
is always an invalid number of loops to quit. Bash will display an error message for that, but you can mute it by redirecting to 2>/dev/null
.
However note also that this behavior is not properly POSIX compliant, because break
is considered a special built-in, and a "special built-in" incurring into error should exit a non-interactive shell (e.g. a script).
1[...]. Changes made to the subshell environment cannot affect the shell’s execution environment. [...]
for
loop inside thefor
loop; can you elaborate what you're actually trying to do?for
loop are missing?for
loop by testing each value for0
; since theif
statement inside thefor
loop only sees one value at a time, it cannot shortcut. Unless you want to exit the loop whenever you see a "false" value (such as1
here)?$cond = 0
? Such that it can be chained with&&
to the next command? I think I got it:$ (for cond in {0,1,0}; do if [[ $cond = 0 ]]; then true; else false; exit; fi; done) && echo "true" || echo "false" false
PS: from here: stackoverflow.com/questions/14059342/…