Reputation: 1089
The process substitution works with filenames fine, e.g. both
$ cat <FILENAME
and
$ while read i; do echo $i; done <FILENAME
work.
But if instead of FILENAME we use echo
command (or any other, which generates output to stdout), cat
continues to work
$ cat <(echo XXX)
XXX
while the loop
$ while read i; do echo $i; done <(echo XXX)
bash: syntax error near unexpected token `<(echo XXX)'
produces error.
Any ideas why?
Upvotes: 27
Views: 9211
Reputation: 241761
Note: <filename
is not process substitution. It's a redirection. Process substitution has the format <(command)
.
Process substitution substitutes the name of a process for the <(...)
. Despite the use of the < symbol, it is not a redirect.
So when you say cat <(echo foo)
, bash creates a subprocess to run the echo
command, and substitutes the name of a pseudo-file which can be read to get the output of that command. The result of the substitution will be something like this:
cat /dev/fd/63
Note the absence of a redirect. (You can see this in action by typing echo <(echo foo)
.)
Like many utilities, cat
can be invoked with or without a command-line argument; if no file is specified, then it reads from stdin
. So cat file.txt
and cat < file.txt
are very similar.
But the while
command does not accept additional arguments. So
while read -r line; do echo "$line"; done < file.txt
is valid, but
while read -r line; do echo "$line"; done file.txt
is a syntax error.
Process substitution doesn't change that. So
while read -r line; do echo "$line"; done /dev/fd/63
is a syntax error, and consequently so is
while read -r line; do echo "$line"; done <(echo foo)
To specify the redirect from the process substitution, you need a redirect:
while read -r line; do echo "$line"; done < <(echo foo)
Note that there must be a space between the two < symbols to avoid confusion with the "here-doc" syntax, <<word
.
Upvotes: 59
Reputation: 753970
The process substitution operation generates a file name, usually in /dev/fd
though that isn't mandated. It needs to be thought of in those terms; it can be used where a file name can be used.
You can see what it does with, for example:
echo <(cat /etc/passwd)
Your examples show this. The cat
command accepts file name arguments. The done
keyword does not accept file name arguments, but does accept I/O redirection.
You can decide that the notation is confusing; in some respects, it is. But there is little point in complaining. That is the way Bash does it. You are at liberty to create your own shell using a different notation, but Bash will continue to support the current notation for reasons of backwards compatibility.
Upvotes: 2
Reputation: 1089
I apologize for stupidity, just have found the working solution
$ while read i; do echo $i; done < <(echo XXX)
XXX
I will be really thankfull if somebody can explain the idea behind this strange syntax with double less sign, < <
.
Upvotes: 5