Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why to always use "if [[ ... ]]; then; fi" instead of a conditional statement #22

Open
stuartpb opened this issue Jan 13, 2015 · 7 comments

Comments

@stuartpb
Copy link

Statements joined with && don't end a script even when it's running with set -e, but if they're the last statement in your file, their non-zero exit code will be your script's exit code (think of it like an implicit return), and if that script was getting called by something with set -e, the whole house of cards will come tumbling down.

In other words, while it's an easily-avoided ledge, it still needs a railing, because a single careless bump will send you way the hell crash tumbling down.

I used to remember this pitfall, but then I went away for a few months, and when I came back I couldn't remember why I didn't do this, and I did it all over my code, and immediately got bitten by this. In general, if you're using set -e, only use tests to test things.

@stuartpb
Copy link
Author

Like, my boilerplate header in Plushu is

set -eo pipefail; [[ -n "$PLUSHU_TRACE" ]] && set -x

and right now I'm considering changing that, because if the rest of the file is empty and tracing isn't enabled, this will crash its caller. (And then you have a wonderful Heisenbug where the problem only manifests itself when debugging isn't on!)

stuartpb added a commit to plushu/plushu-cleanup-old-app-images that referenced this issue Jan 13, 2015
@progrium
Copy link
Owner

If the rest of the file is empty? When would you have a script that has nothing but this? Otherwise, it seems totally reasonable to understand this behavior. I can imagine maybe a tip to remind people that this happens, but I don't think it's worth a rule against conditional expressions.

@stuartpb
Copy link
Author

If the rest of the file is empty? When would you have a script that has nothing but this?

When I'm creating stub scripts before filling them in.

When would you have a script that has nothing but this? Otherwise, it seems totally reasonable to understand this behavior. I can imagine maybe a tip to remind people that this happens, but I don't think it's worth a rule against conditional expressions.

Okay, so in the middle of responding to a long-standing issue that's been sitting in the queue for weeks/months/years, you're refactoring a file that does this at the end:

[[ -f "$EXTRA_STUFF_FILE" ]] && cat "$EXTRA_STUFF_FILE"

printf "FINAL_VAR=%q\n" "$(finalization_code)"

To do this instead:

printf "FINAL_VAR=%q\n" "$(finalization_code)"

[[ -f "$EXTRA_STUFF_FILE" ]] && cat "$EXTRA_STUFF_FILE"

Is this specific combination of caveats (that Bash exits with the last exit code normally, and that set -e doesn't exit with &&ed expressions), which you last thought about four months ago, going to occur to you right now, in this exact moment where you're preoccupied with a design refactor? Or is it going to come to you later, after an hour of debugging with various env vars cleared and set, clobbering configs, turning it off and back on again, spinning it upside down and shaking it, flipping the "magic" switch to "more magic", and just generally not recognizing why the calling script is suddenly tanking out of nowhere?

(Hint: I went through this 36 hours ago. It's the second one.)

@progrium
Copy link
Owner

Curious about your debugging process for this ... if you had tracing on, wouldn't that lead to this line? I guess not if your outer scripts suppress the nonzero exit? I'm imagining that -f failed causing it to exit, bubbling up to stop the command there, and if you're tracing it would show you that line and you'd realize what's going on.

@stuartpb
Copy link
Author

Except tracing doesn't show any difference between tests inside conditions and tests outside them, so all you see is a series of -f, -e, -z, and/or -n tests, that suddenly ends in the middle of the thread of execution for no apparent reason. And then, yeah, you can go into the file, and step backwards through your logic to find the exact combination of circumstances that led to this code path being taken, and then see that this was the last command that was executed and maybe understand that this is what happened if you're already super well versed in every weird thing Bash does, including a behavior you will never otherwise see because set -e doesn't normally allow expressions that return non-zero.

Or you could just take the ten extra keystrokes it takes to type if ; then ; fi in the first place and never even see this problem because all your mid-script exit codes are 0.

@stuartpb
Copy link
Author

Not to mention always using if is, stylistically, more consistent (and isn't consistent style the name of the game here?)

@progrium
Copy link
Owner

Okay, I'm starting to see the light...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants