<<< text
is a redirection, if affects the file descriptors of a command (here 0, but 4<<< text
would affect the fd 4 instead).
While <(...)
is an expansion/substitution, not a redirection. That expands to a file name. That affects the arguments passed to a command if inserted there.
cat <(echo test)
Runs cat
with a file like /dev/fd/12
as argument. And as it happened also with its fd 12 being open on the reading end of a pipe. cat
will not read from its fd 12, but when it opens the /dev/fd/12
file, it will get a new fd (maybe 3) that points to the same pipe as fd 12.
Meanwhile echo test
is run in background with its fd 1 connected to the writing end of that same pipe, so echo
and cat
will end up being connected via that pipe (though the establishment of that communication channel is much more convoluted than with echo text | cat
).
cat <<< text
Runs cat without argument and with its fd 0 open for reading on a deleted temporary file that contains test\n
.
zsh interprets
<<< test
as
$NULLCMD <<< test
($NULLCMD
being typically cat
) as happens any time you run something with only redirections (except when there's only one <
redirection, in which case $READNULLCMD
(typically a pager) is used instead)).
In:
<(echo text)
There's no redirection, just one process substitution that expands to one /dev/fd/12 argument. So that's asking the shell to execute that file.
In zsh, there's no real benefit of using:
cat <(echo test)
cat < <(echo test)
over
echo test | cat
You see this construct in other shells like bash in things like:
IFS= read -r var < <(echo text)
but that's because in those shells, echo text | IFS= read -r var
would not work because read
is run in a child process. That's not the case in zsh
.
Process substitution is useful for commands that only accept input via a file name as argument (and not via stdin) of when you need to pass more than one input to a command (there's only one stdin) like in diff <(cmd1) <(cmd2)
.
One difference in zsh
between cmd1 | cmd2
and cmd2 < <(cmd1)
is that in the latter, zsh
doesn't wait for cmd1
. While it does wait for both cmd1
and cmd2
in cmd1 | cmd2
(if only so as to make both exit statues available in $pipestatus
. Compare sleep 1 | uname
with uname < <(sleep 1)
.
cmd <<< foo
can be useful when cmd
needs to be able to seek into it's input (because in zsh
, here-strings like here-documents are implemented with temporary files instead of pipes).
<()
is a sub-shell being replaced with a string, and is not being attached to stdin of the process, whereas<<<
does attach to stdin like<
does, but I'm not 100% on this, so an authoritative answer that explains the fundamentals would be beneficial to many I think! – Alex Forbes Aug 09 '19 at 12:36<()
is "process substitution",<<<
is a herestring. Look up the docs for those and you will see they're quite unrelated. – muru Aug 09 '19 at 12:46