why process substitution does not always work with while loop in bash? why process substitution does not always work with while loop in bash? bash bash

why process substitution does not always work with while loop in bash?


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.


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, < <.


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.