I have seen multiple people point it out as confusing. The problem is that in the first case, there are two exit codes:
1. false (suppressed on LHS of &&)
2. true
And in the second case, there are 3:
1. false (suppressed on LHS of &&)
2. true
3. the exit code of the function/subshell, which is the exit code of the last statement, which is nonzero. This then CAUSES THE PROGRAM TO FAIL, whereas it didn't before.
(edit: removed incorrect code)
-----
So it's kind of like a "ghost" exit code created by the subshell/function! Which has an unexpected interaction with errexit.
Does that make sense?
I am not sure exactly how to fix it, but it will probably involve limiting && to stuff like this:
if test -d /tmp && test -d /tmp/foo; then echo yes; fi
And disallow it when standing alone, because it doesn't make much sense there, if errexit is on.
Feel free to chime in bug with any possible solutions or more problems.
----
(And yes Oil fixes the brace problem with shopt -s parse_brace, which is on by default in Oil! Try it out and let me know if you like it :) )
Not at all well, I'm afraid, but I think I understand:
f(){ false && true; } ; set -e ; f ; echo hi
# this works correctly (f returns false and errexit triggers)
set -e; false && true ; echo hi
# despite `false && true` failing, this doesn't trigger errexit
I was confused by the implication that the function version was what was wrong (rather than "false && true is broken, and you'd expect the function to work the same way"). Probably partly because I was confusing it with `command || true`, which is a idiomatic way to suppress errexit-like mechanisms by making the exit status always 0.
test -d nosuchdir && echo "warning: no directory";
instead of
false && true
You didn't want the function to fail. You just wanted to print a warning if the directory doesn't exist. But the whole function fails, while it doesn't for the if statement.
The whole thing is inherently confusing ... The language confuses success/failure and true/false. Both of those are stuffed into an exit code.
test -d is really for true/false, but then functions also have success/failure (did the last command succeed).
So there's no way for shell to really know what you want.
I think Oil might end up with something like "if catch foo" for success/fail, and "if boolean grep" for true/false.
> You just wanted to print a warning if the directory doesn't exist.
That code prints a warning if the directory does exist, actually ("-d FILE FILE exists and is a directory"). Did you mean something like:
test -e outfile && echo "trying to overwrite preexisting outfile"
> You didn't want the function to fail.
Uh, I kind of do, actually? If I wanted to discard the exit status I'd write:
test -e outfile && echo "trying to overwrite preexisting outfile" || true;
This is awkward, and suggests a proper if-then operator (perl-6-style `??` collides with globbing, but maybe `&?`?), but it's better then having something like:
mostly-silently mix old and new data together because mkfoo failed and the error code was swallowed.
> So there's no way for shell to really know what you want.
Yep, and if we have to pick one problem, things failing completely in a obvious and also easily-fixable way is usually a much less awful problem to have than silently corrupting data and state, especially when you've opted-in to a mechanism (like errexit) specifically designed to do the former.
https://news.ycombinator.com/item?id=24738274
except with a shell function rather than a subshell.
Demo:
https://github.com/oilshell/blog-code/blob/master/errexit /demo.sh#L21
Run ./demo.sh subshell-compare and func-compare.
I have seen multiple people point it out as confusing. The problem is that in the first case, there are two exit codes:
1. false (suppressed on LHS of &&)
2. true
And in the second case, there are 3:
1. false (suppressed on LHS of &&)
2. true
3. the exit code of the function/subshell, which is the exit code of the last statement, which is nonzero. This then CAUSES THE PROGRAM TO FAIL, whereas it didn't before.
(edit: removed incorrect code)
-----
So it's kind of like a "ghost" exit code created by the subshell/function! Which has an unexpected interaction with errexit.
Does that make sense?
I am not sure exactly how to fix it, but it will probably involve limiting && to stuff like this:
And disallow it when standing alone, because it doesn't make much sense there, if errexit is on.Feel free to chime in bug with any possible solutions or more problems.
----
(And yes Oil fixes the brace problem with shopt -s parse_brace, which is on by default in Oil! Try it out and let me know if you like it :) )