While process substitution is great on the surface, it comes with a troubling downside: There is no way, that I have found, to reliably catch errors. That is, this:
some-command <(some-failing-command)
...will succeed. And -e and pipefail do nothing here. I have not found any way to push the error up. Plenty of questions on Stackoverflow and elsewhere, no good answers.
Isn't that because the syntax provided doesn't use a pipe? Which is why actually using a pipe, rather than paren redirection, is easier on the eyes and the shell. It reads more like the flow of text:
maybe_failing() {
if ! some-failing-command; then
echo "That did not shake out" >&2
return 1
fi
}
maybe_failing | some-command
Is that one of the things StackOverflow said? And if so, why is it not a "good" answer (verbosity concerns aside)?
This is the point at which one reaches for tempfiles, usually.
The point remains that <() was made for a purpose, yet lacks error handling, thus undermining its usefulness to the point of uselessness.
Edit: Isn't substitution using pipes, though? As in FIFO pipes? You get a file descriptor device which is closed at the end of the script. I don't know if the fd is wired directly to the command's stdout or whether the output is written in its entirety to a hidden tempfile first; the former sounds more natural and efficient.
>I don't know if the fd is wired directly to the command's stdout or whether the output is written in its entirety to a hidden tempfile first; the former sounds more natural and efficient.
Nope. <() and >() is equivalent to creating a named pipe with mkfifo and writing to it:
But, the multiple case example highlights that if (for argument's sake) `make-input` and `make-more-stuff` had run _prior_ to that line, writing their (possibly empty) output to a (file|FIFO), how then would you want bash to behave? It would still open file descriptors to those (file|FIFO)s, which would still be just as blank.
It seems to me that if one wishes more fine grained control over the error handling in a multi-subshell-fd-trickery situation, then creating the FIFO(s) and managing the sender's exit status is the supervising script's responsibility.
a file descriptor device which is closed at the end of the script
I checked, and it's actually not even at the end of the script; those fds only exist for that one child, as `process-stuff` is exec-ed by bash.
whether the output is written in its entirety to a hidden tempfile
It's not a temp-file, it's an actual file descriptor, which bash `dup2`s for the subprocess, then cheekily uses the `/dev/fd/63` syntax to make appear as a file; you can peer into its brain a little:
$ showme() {
echo "showme.args=$@" >&2
the_fd="${1##/dev/fd/}"
cat <&${the_fd}
}
$ showme <(date -u)
showme.args=/dev/fd/63
Sat Jan 6 21:31:25 UTC 2018
Thanks so much for highlighting this scenario; I learned a ton about how that works researching this answer. That's why I like answering stuff on S.O., too: win-win
Good point about how to abort. Maybe it would be possible to short-circuit the fd somehow, so that the reader got an EOF, and that would in turn cause failure in the program reading the stream. At the same time, any success code from the program should be turned into an error exit code so the script could detect the failure. Not perfect, but arguably better than no failure at all.
Parameter substitution is far from intuitive. Every time I have to open one of my older shell scripts (>3 months ago), I'm thankful I wrote comments. Otherwise I'd have to man/google things again. I really wish they'd use function names or something, instead (sub, etc.).
My "mnemonic" is that # means prefix, because every shell script starts with a shebang too. From this I can deduce that ## means longest prefix, therefore % and %% means suffix.
- use the unofficial strict mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/
- use parameter substitutions like ${foo#prefix}, ${foo%suffix} instead of invoking sed/awk
- process substitution instead of named pipes: <(), >()
- know the difference between an inline group {} and a subshell ()
- use printf "%q" when passing variables to another shell (e.g. assembling a command locally and executing it via SSH)
reply