Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puzzling Bash syntax -- redirect into loop

Running a script through shellcheck.net I was told that "for loops over find output are fragile." Fair enough, filenames with spaces are always tricky.

The suggested replacement code (one of them anyway) was this:

while IFS= read -r -d '' file
do
    #do stuff
done <   <(find . -name '*' -print0)

I'm familiar with the < operator used to input text from a file to a command, and clearly the results from find are being fed into the loop. So when I first saw this, I thought it was victim to some formatting problems and tried "cleaning up" the last line. But removing the whitespace between < < resulted in syntax errors, as did adding one into <(.

So what is the < <(cmd) construct doing? Apologies if this is a simple or redundant question, it's not easy to search for these things!

like image 726
miken32 Avatar asked Feb 08 '23 12:02

miken32


2 Answers

The < is input redirection as you say.

The <( is Process Substitution.

Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. It takes the form of

<(list)

or

>(list)

The process list is run with its input or output connected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current command as the result of the expansion. If the >(list) form is used, writing to the file will provide input for list. If the <(list) form is used, the file passed as an argument should be read to obtain the output of list. Note that no space may appear between the < or > and the left parenthesis, otherwise the construct would be interpreted as a redirection.

When available, process substitution is performed simultaneously with parameter and variable expansion, command substitution, and arithmetic expansion.

You can't combine the two < marks because then that becomes a here document token.

You can't add a space between < and ( because then it stops being the process substitution token.

like image 126
Etan Reisner Avatar answered Feb 23 '23 03:02

Etan Reisner


A while loop in bash is an example of a compound command. Like any other command, it has its own standard input. It doesn't actually read from it at all, but any command run from within the while loop will inherit its standard input from the loop.

In this example, the read command will read from the output of the find command.


<(find ...) is an example of process substitution. The find command is run, and the <(...) is treated like a file whose contents are the output of the find command. It is this "file" that the while loop (and therefore read) uses for its standard input.

like image 28
chepner Avatar answered Feb 23 '23 05:02

chepner