💾 Archived View for thrig.me › blog › 2023 › 09 › 29 › shell-logic.gmi captured on 2024-08-18 at 18:05:42. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-11-14)
-=-=-=-=-=-=-
Complex logic can be expressed in unix shell scripts using || and && though probably should not. For one, what is the operator precedence and associativity?
$ false || echo foo && echo bar foo bar
If one wants to do two different things depending on the exit status word of the first command (the "false", in the above example) there can be traps for the unwary.
$ false && echo first-okay || echo first-fail first-fail $ true && echo first-okay || echo first-fail first-okay
This appears to work though you may want to work through the truth table. In particular the following shows an inappropriate failure when the second script fails.
$ true && false || echo first-fail first-fail
Critics will point out that "echo" is unlikely to fail, though some other more complicated command could easily return a non-zero exit status word. Perhaps "report-okay" in the following example unexpectedly fails; this will create a case where the ping-test was okay but the report-failure runs.
#!/bin/sh ping-test && report-okay || report-failure
Monitoring systems that "cry wolf" too much generally will be ignored.
As the bands slipped off the vats two or three times a year, Crick was unconcerned. He told his supervisor about the problem, but was told "that no harm whatever would ensue".
https://en.wikipedia.org/wiki/London_Beer_Flood
"echo" or other scripts not expected to fail may still do so; one may want to test the scripts under a situation where the system is unable to fork more processes. Or, common file descriptors could be closed thus causing even the simple "echo" to fail. Maybe someone forgot to check whether a pipe(2) or dup(2) call failed, and now standard out is closed? These are unusual situations, but they can and do happen in production, and usually result in a sysadmin being woken up at 2 AM in the morning. Ask me how I know!
$ ( true && echo foo || echo >&2 error ) >&- error
The inability to fork processes (EAGAIN, ENOMEM) or running out of file descriptors (EMFILE) can also cause problems, especially on systems that set low limits (OpenBSD comes to mind), or on systems that are very busy due to a large number of users. Modern hardware is generally beefy enough to allow for pretty generous limits, but there are still various ceilings to bump into, even if they have been set higher these days. Or the code works, but then is moved to a tiny virtual system.
Systems that make heavy use of shell scripts may be more likely to run into fork or file descriptor limits; shell scripts may use lots of file descriptor redirections and can fork lots and lots of processes which is, generally, not very efficient versus a program that does not fork out so much or at all.
On the complex logic front, you probably want an if/else construct. Probably also the code should be spread out over several lines, too. Critical monitoring code is not a good place to be "golfing" and cutting down on the keystrokes necessary.
#!/bin/sh if ping-test ; then report-okay ; else report-failure ; fi
tags #sh #unix