Process substitution is the counterpart to command substitution. Command substitution sets a variable to the result of a command, as in dir_contents=`ls -al` or xref=$( grep word datafile). Process substitution feeds the output of a process to another process (in other words, it sends the results of a command to another command).
>(command)
<(command)
These initiate process substitution. This uses /dev/fd/<n> files to send the results of the process within parentheses to another process. [1]
There is no space between the the "<" or ">" and the parentheses. Space there would give an error message. |
bash$ echo >(true) /dev/fd/63 bash$ echo <(true) /dev/fd/63 |
Process substitution can compare the output of two different commands, or even the output of different options to the same command.
bash$ comm <(ls -l) <(ls -al) total 12 -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh total 20 drwxrwxrwx 2 bozo bozo 4096 Mar 10 18:10 . drwx------ 72 bozo bozo 4096 Mar 10 17:58 .. -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh |
Using process substitution to compare the contents of two directories (to see which filenames are in one, but not the other):
1 diff <(ls $first_directory) <(ls $second_directory) |
Some other usages and uses of process substitution:
1 cat <(ls -l) 2 # Same as ls -l | cat 3 4 sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin) 5 # Lists all the files in the 3 main 'bin' directories, and sorts by filename. 6 # Note that three (count 'em) distinct commands are fed to 'sort'. 7 8 9 diff <(command1) <(command2) # Gives difference in command output. 10 11 tar cf >(bzip2 -c > file.tar.bz2) $directory_name 12 # Calls "tar cf /dev/fd/?? $directory_name", and "bzip2 -c > file.tar.bz2". 13 # 14 # Because of the /dev/fd/<n> system feature, 15 # the pipe between both commands does not need to be named. 16 # 17 # This can be emulated. 18 # 19 bzip2 -c < pipe > file.tar.bz2& 20 tar cf pipe $directory_name 21 rm pipe 22 # or 23 exec 3>&1 24 tar cf /dev/fd/4 $directory_name 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&- 25 exec 3>&- 26 27 28 # Thanks, Stepane Chazelas |
A reader sent in the following interesting example of process substitution.
1 # Script fragment taken from SuSE distribution: 2 3 while read des what mask iface; do 4 # Some commands ... 5 done < <(route -n) 6 7 8 # To test it, let's make it do something. 9 while read des what mask iface; do 10 echo $des $what $mask $iface 11 done < <(route -n) 12 13 # Output: 14 # Kernel IP routing table 15 # Destination Gateway Genmask Flags Metric Ref Use Iface 16 # 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo 17 18 19 20 # As Stephane Chazelas points out, an easier-to-understand equivalent is: 21 route -n | 22 while read des what mask iface; do # Variables set from output of pipe. 23 echo $des $what $mask $iface 24 done # This yields the same output as above. 25 # However, as Ulrich Gayer points out . . . 26 #+ this simplified equivalent uses a subshell for the while loop, 27 #+ and therefore the variables disappear when the pipe terminates. 28 29 30 31 # However, Filip Moritz comments that there is a subtle difference 32 #+ between the above two examples, as the following shows. 33 34 ( 35 route -n | while read x; do ((y++)); done 36 echo $y # $y is still unset 37 38 while read x; do ((y++)); done < <(route -n) 39 echo $y # $y has the number of lines of output of route -n 40 ) 41 42 More generally spoken 43 ( 44 : | x=x 45 # seems to start a subshell like 46 : | ( x=x ) 47 # while 48 x=x < <(:) 49 # does not 50 ) 51 52 # This is useful, when parsing csv and the like. 53 # That is, in effect, what the original SuSE code fragment does. |
[1] | This has the same effect as a named pipe (temp file), and, in fact, named pipes were at one time used in process substitution. |