Fun fact, in bash you lose the exit codes of process subs. It doesn't even wait() on those processes in many cases.<p>So there is no way to abort a bash script if something like <(sort nonexistent) fails.<p>OSH lets you opt into stricter behavior:<p><pre><code> $ osh -c
set -e
shopt -s oil:basic
diff <(sort left) <(sort nonexistent)
echo "should not get here"'
'
sort: cannot read: nonexistent: No such file or directory
diff <(sort left) <(sort nonexistent)
^~
[ -c flag ]:1: fatal: Exiting with status 2 (command in PID 29359)
</code></pre>
In contrast, bash will just keep going and ignore failure.<p>You can also get all the exit codes with @_process_sub_status, which is analogous to PIPESTATUS.<p>(I should probably write a blog post about this: <a href="http://www.oilshell.org/blog/" rel="nofollow">http://www.oilshell.org/blog/</a>)
A fun trick is using the `paste` command with process substitution to combine lines of output from different commands.<p><pre><code> ./cmd1</code></pre>
outputs:<p><pre><code> a
b
c
./cmd2</code></pre>
outputs:<p><pre><code> 1
2
3
paste <(./cmd1) <(./cmd2)</code></pre>
outputs:<p><pre><code> a 1
b 2
c 3
</code></pre>
You can then use xargs to use each line as arguments for another command:<p><pre><code> paste <(./cmd1) <(./cmd2) | xargs -L1 ./cmd3</code></pre>
calls:<p><pre><code> ./cmd3 a 1
./cmd3 b 2
./cmd3 c 3</code></pre>
I love zsh’s take on process substitution:<p><pre><code> cat =(echo foo)
</code></pre>
It’s almost identical to <(echo foo), but crucially it drains the output before performing the substitution. It’s like <(... | sponge), but somehow much more reliable.<p>I’ve used it in a few situations where process substation failed, though being old and feeble I can’t remember why it failed. But for example you can copy the file safely, knowing that it will contain the full output of the shell pipeline, unlike process substitution.<p>I don’t even know the name of =(...). Hmm. Sponge substitution?
I quite like how fish does process substitution.<p><pre><code> foo (bar | psub)
</code></pre>
Instead of syntax it's just a plain function.<p>docs: <a href="https://fishshell.com/docs/current/cmds/psub.html" rel="nofollow">https://fishshell.com/docs/current/cmds/psub.html</a>
source of psub.fish: <a href="https://github.com/fish-shell/fish-shell/blob/master/share/functions/psub.fish" rel="nofollow">https://github.com/fish-shell/fish-shell/blob/master/share/f...</a>
The usability of unixy shells generally falls down a cliff when you need to deal with more than just one input one output. The awkwardness in trying to shoehorn process substitution is just one of the examples of that.
Great read, but worth noting from the end of the article that "late feature" here means it was added in the early '90s. The late addition to Unix that surprises me is ssh. It was only invented in the late '90s. Before that everyone used unencrypted remote shells like telnet.<p>Encryption in general was in a pretty bad state in the '90s: original http was unencrypted and early mobile phone standards that are still in use have very weak encryption.
For whatever reason I can never remember the syntax of <(command) and end up "rediscovering" it every year. It's seldom used but when it's needed it's rather elegant.<p>Another somewhat related useful bash feature is using this form:<p><pre><code> wc <<< 'a b c'
</code></pre>
instead of:<p><pre><code> echo 'a b c' | wc</code></pre>
The claim is false; process substitution can be cobbed together with named fifos,* and those are "ancient".<p>Only problem that those are temporary objects that have to be created in the file system, and cleaned up.<p>However, temporary objects (files, no fifos) are also used in here doc implementations.<p>Process substitution is a late feature simply because the creativity juice in Unix (tm) dried up some time before the middle 1990's, leaving the FOSS reimplementations of Unix to carry the development torch.<p>Those projects had to balance among other goals like quality/robustness and compatibility.<p>(If we look at the quality of the FOSS tools compared to the Unix originals, we could also remark that "quality and robustness was late in coming to Unix". But we equivocate on Unix, because GNU stands for GNU is Not Unix!)<p>Features appearing in FOSS utilities like GNU Bash take time to make into Unix (tm).<p>Process substitution is not yet in the standard, therefore it is not in in fact in Unix (tm).<p>Shell scripting is a conservative activity. The language isn't very good and so improving it is like kicking a dead horse in some ways; the most important matter in any new shell release is that old scripts keep working. (Like configuration scripts for the build systems of nicer languages).<p>---<p>* See GNU Bash manual: <a href="https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Process-Substitution" rel="nofollow">https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash....</a>:
<i>" Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. "</i>
In practice I end up caching the output often. I have used process substitution but the iteration process feels more useful to me if I've slowly built up data and I can inspect the internal pieces each time and reuse them in different ways.<p>But I can see if it's relatively fast. I like it. I just don't end up using it often.
0. Process substitution is a potential DoS vector as it could take up all of RAM and/or disk space.<p>1. Also, not all commands are compatible with it, especially if they need rewinding or reopening. diff has issues with using it for both arguments often. It's likely the use of memory mapped files, but I could be wrong.<p>2. Shells ought to implement a flag for process substitution to allow temporary files to reside on disk for the lifetime of the command line. This way, it can operate on extremely large files.
An unfortunate thing is that process substitution does not work in git bash on Windows. (at least it was the case last time I tested; googling around I found a random comment in random github repo saying it's been fixed in 2.25 but I don't have a laptop handy to test it now).