The cmd | read -r var1 var2
construct famously does not work in bash because the read command is executed in a subshell due to piping. I used to use read -r var1 var2 <<< "$(cmd)"
to get around this, but recently I learned about the cmd_drain < <(cmd_src)
construct, which seems to work just as well: read -r var1 var2 < <$(cmd)
.
Is there a difference between these two solutions? There does not seem to be any difference in the trivial case:
$ hd < <(echo Hello)
00000000 48 65 6c 6c 6f 0a |Hello.|
00000006
$ hd <<< $(echo Hello)
00000000 48 65 6c 6c 6f 0a |Hello.|
00000006
I also tried some special characters and got the same results. My gut feeling is that the result will always be the same expect that cmd_drain <<< "$(cmd_src)"
will first run cmd_src
and buffer the whole result in memory before feeding it to cmd_drain
, while cmd_drain < <(cmd_src)
will continously feed the output of cmd_src
into cmd_drain
. I assume it behaves like cmd_src | cmd_drain
except that cmd_src
will be run in a sub-shell instead of cmd_drain
. Is my assumption correct?
Bonus question: Is quoting necessary around the $()
construct?
Best Answer
Yes, your assumption is correct. In
cmd_drain < <(cmd_src)
(aka Process Substitution, combined with normal redirection), Bash will replace<(cmd_src)
with the path to a file, from which the output ofcmd_src
can be read. From the docs:In
cmd_drain <<< "$(cmd_src)"
,<<< ...
is treated like any other here-string, so:So you don't need to quote
$()
there, but specifically because the here string<<<
syntax doesn't do word splitting or filename expansion. Usually, you'd have to.Note again the last sentence of the here string documentation - a newline is appended:
Whether or not that matters is up to what you're running.
In
hd <<< $(echo Hello)
, the command substitution removes the trailing newline output byecho
, and the here string adds a newline, effectively giving you the same output. But, as the above example shows, this removal/addition of newlines can be tricky, and you need not get exactly whatcmd_src
output.