📄 process-sub.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><HTML><HEAD><TITLE>Process Substitution</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+"><LINKREL="HOME"TITLE="Advanced Bash-Scripting Guide"HREF="index.html"><LINKREL="UP"TITLE="Advanced Topics"HREF="part5.html"><LINKREL="PREVIOUS"TITLE="Restricted Shells"HREF="restricted-sh.html"><LINKREL="NEXT"TITLE="Functions"HREF="functions.html"><METAHTTP-EQUIV="Content-Style-Type"CONTENT="text/css"><LINKREL="stylesheet"HREF="common/kde-common.css"TYPE="text/css"><METAHTTP-EQUIV="Content-Type"CONTENT="text/html; charset=iso-8859-1"><METAHTTP-EQUIV="Content-Language"CONTENT="en"><LINKREL="stylesheet"HREF="common/kde-localised.css"TYPE="text/css"TITLE="KDE-English"><LINKREL="stylesheet"HREF="common/kde-default.css"TYPE="text/css"TITLE="KDE-Default"></HEAD><BODYCLASS="CHAPTER"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#AA0000"VLINK="#AA0055"ALINK="#AA0000"STYLE="font-family: sans-serif;"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="restricted-sh.html"ACCESSKEY="P">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="functions.html"ACCESSKEY="N">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="PROCESS-SUB"></A>Chapter 22. Process Substitution</H1><P><ANAME="PROCESSSUBREF"></A><AHREF="special-chars.html#PIPEREF">Piping</A> the <TTCLASS="FILENAME">stdout</TT> of a command into the <TTCLASS="FILENAME">stdin</TT> of another is a powerful technique. But, what if you need to pipe the <TTCLASS="FILENAME">stdout</TT> of <SPANCLASS="emphasis"><ICLASS="EMPHASIS">multiple</I></SPAN> commands? This is where <TTCLASS="REPLACEABLE"><I>process substitution</I></TT> comes in.</P><P><ICLASS="FIRSTTERM">Process substitution</I> feeds the output of a process (or processes) into the <TTCLASS="FILENAME">stdin</TT> of another process.</P><DIVCLASS="VARIABLELIST"><P><B><ANAME="COMMANDSPARENS1"></A>Template</B></P><DL><DT>Command list enclosed within parentheses</DT><DD><P><BCLASS="COMMAND">>(command_list)</B></P><P><BCLASS="COMMAND"><(command_list)</B></P><P>Process substitution uses <TTCLASS="FILENAME">/dev/fd/<n></TT> files to send the results of the process(es) within parentheses to another process. <ANAME="AEN16838"HREF="#FTN.AEN16838">[1]</A> </P><DIVCLASS="CAUTION"><TABLECLASS="CAUTION"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="common/caution.png"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>There is <SPANCLASS="emphasis"><ICLASS="EMPHASIS">no</I></SPAN> space between the the <SPANCLASS="QUOTE">"<"</SPAN> or <SPANCLASS="QUOTE">">"</SPAN> and the parentheses. Space there would give an error message.</P></TD></TR></TABLE></DIV></DD></DL></DIV><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="SCREEN"> <TTCLASS="PROMPT">bash$ </TT><TTCLASS="USERINPUT"><B>echo >(true)</B></TT> <TTCLASS="COMPUTEROUTPUT">/dev/fd/63</TT> <TTCLASS="PROMPT">bash$ </TT><TTCLASS="USERINPUT"><B>echo <(true)</B></TT> <TTCLASS="COMPUTEROUTPUT">/dev/fd/63</TT> </PRE></TD></TR></TABLE> Bash creates a pipe with two <AHREF="io-redirection.html#FDREF">file descriptors</A>, <TTCLASS="FILENAME">--fIn</TT> and <TTCLASS="FILENAME">fOut--</TT>. The <TTCLASS="FILENAME">stdin</TT> of <AHREF="internal.html#TRUEREF">true</A> connects to <TTCLASS="FILENAME">fOut</TT> (dup2(fOut, 0)), then Bash passes a <TTCLASS="FILENAME">/dev/fd/fIn</TT> argument to <BCLASS="COMMAND">echo</B>. On systems lacking <TTCLASS="FILENAME">/dev/fd/<n></TT> files, Bash may use temporary files. (Thanks, S.C.)</P><P>Process substitution can compare the output of two different commands, or even the output of different options to the same command.</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="SCREEN"> <TTCLASS="PROMPT">bash$ </TT><TTCLASS="USERINPUT"><B>comm <(ls -l) <(ls -al)</B></TT> <TTCLASS="COMPUTEROUTPUT">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</TT></PRE></TD></TR></TABLE><P><ANAME="PCC2DIR"></A></P><P> Using process substitution to compare the contents of two directories (to see which filenames are in one, but not the other): <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 diff <(ls $first_directory) <(ls $second_directory)</PRE></TD></TR></TABLE> </P><P>Some other usages and uses of process substitution:</P><P><ANAME="PSFDSTDIN"></A></P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 read -a list < <( od -Ad -w24 -t u2 /dev/urandom ) 2 # Read a list of random numbers from /dev/urandom, 3 #+ process with "od" 4 #+ and feed into stdin of "read" . . . 5 6 # From "insertion-sort.bash" example script. 7 # Courtesy of JuanJo Ciarlante.</PRE></TD></TR></TABLE></P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 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, St閜hane Chazelas</PRE></TD></TR></TABLE></P><P>A reader sent in the following interesting example of process substitution.</P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 # Script fragment taken from SuSE distribution: 2 3 # --------------------------------------------------------------# 4 while read des what mask iface; do 5 # Some commands ... 6 done < <(route -n) 7 # ^ ^ First < is redirection, second is process substitution. 8 9 # To test it, let's make it do something. 10 while read des what mask iface; do 11 echo $des $what $mask $iface 12 done < <(route -n) 13 14 # Output: 15 # Kernel IP routing table 16 # Destination Gateway Genmask Flags Metric Ref Use Iface 17 # 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo 18 # --------------------------------------------------------------# 19 20 # As St閜hane Chazelas points out, 21 #+ an easier-to-understand equivalent is: 22 route -n | 23 while read des what mask iface; do # Variables set from output of pipe. 24 echo $des $what $mask $iface 25 done # This yields the same output as above. 26 # However, as Ulrich Gayer points out . . . 27 #+ this simplified equivalent uses a subshell for the while loop, 28 #+ and therefore the variables disappear when the pipe terminates. 29 30 # --------------------------------------------------------------# 31 32 # However, Filip Moritz comments that there is a subtle difference 33 #+ between the above two examples, as the following shows. 34 35 ( 36 route -n | while read x; do ((y++)); done 37 echo $y # $y is still unset 38 39 while read x; do ((y++)); done < <(route -n) 40 echo $y # $y has the number of lines of output of route -n 41 ) 42 43 More generally spoken 44 ( 45 : | x=x 46 # seems to start a subshell like 47 : | ( x=x ) 48 # while 49 x=x < <(:) 50 # does not 51 ) 52 53 # This is useful, when parsing csv and the like. 54 # That is, in effect, what the original SuSE code fragment does.</PRE></TD></TR></TABLE></P></DIV><H3CLASS="FOOTNOTES">Notes</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN16838"HREF="process-sub.html#AEN16838">[1]</A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>This has the same effect as a <AHREF="extmisc.html#NAMEDPIPEREF">named pipe</A> (temp file), and, in fact, named pipes were at one time used in process substitution.</P></TD></TR></TABLE><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="restricted-sh.html"ACCESSKEY="P">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html"ACCESSKEY="H">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="functions.html"ACCESSKEY="N">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Restricted Shells</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="part5.html"ACCESSKEY="U">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">Functions</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -