process-sub.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 534 行
HTML
534 行
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><HTML><HEAD><TITLE>进程替换</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINKREL="HOME"TITLE="高级Bash脚本编程指南"HREF="index.html"><LINKREL="UP"TITLE="高级主题"HREF="part4.html"><LINKREL="PREVIOUS"TITLE="受限shell"HREF="restricted-sh.html"><LINKREL="NEXT"TITLE="函数"HREF="functions.html"></HEAD><BODYCLASS="CHAPTER"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">高级Bash脚本编程指南: 一本深入学习shell脚本艺术的书籍</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="restricted-sh.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="functions.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="PROCESS-SUB"></A>22. 进程替换</H1><P><ANAME="PROCESSSUBREF"></A><TTCLASS="REPLACEABLE"><I>进程替换</I></TT>与<AHREF="commandsub.html#COMMANDSUBREF">命令替换</A>很相似. 命令替换把一个命令的结果赋值给一个变量, 比如<BCLASS="COMMAND">dir_contents=`ls -al`</B>或<BCLASS="COMMAND">xref=$( grep word datafile)</B>. 进程替换把一个进程的输出提供给另一个进程(换句话说, 它把一个命令的结果发给了另一个命令). </P><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="COMMANDSPARENS1"></A>命令替换的模版</B></P><DL><DT>用圆括号扩起来的命令</DT><DD><P><BCLASS="COMMAND">>(command)</B></P><P><BCLASS="COMMAND"><(command)</B></P><P>启动进程替换. 它使用<TTCLASS="FILENAME">/dev/fd/<n></TT>文件将圆括号中的进程处理结果发送给另一个进程. <ANAME="AEN14284"HREF="#FTN.AEN14284"><SPANCLASS="footnote">[1]</SPAN></A> (译者注: 实际上现代的UNIX类操作系统提供的<TTCLASS="FILENAME">/dev/fd/n</TT>文件是与文件描述符相关的, 整数n指的就是进程运行时对应数字的文件描述符) </P><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>在<SPANCLASS="QUOTE">"<"</SPAN>或<SPANCLASS="QUOTE">">"</SPAN>与圆括号之间是<EM>没有</EM>空格的. 如果加了空格, 会产生错误. </P></TD></TR></TABLE></DIV></DD></DL></DIV><P> <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">echo >(true)</KBD><SAMPCLASS="COMPUTEROUTPUT">/dev/fd/63</SAMP><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">echo <(true)</KBD><SAMPCLASS="COMPUTEROUTPUT">/dev/fd/63</SAMP> </PRE></FONT></TD></TR></TABLE> Bash在两个<AHREF="io-redirection.html#FDREF">文件描述符</A>之间创建了一个管道, <TTCLASS="FILENAME">--fIn</TT>和<TTCLASS="FILENAME">fOut--</TT>. <AHREF="internal.html#TRUEREF">true</A>命令的<TTCLASS="FILENAME">stdin</TT>被连接到<TTCLASS="FILENAME">fOut</TT> (dup2(fOut, 0)), 然后Bash把<TTCLASS="FILENAME">/dev/fd/fIn</TT>作为参数传给<BCLASS="COMMAND">echo</B>. 如果系统缺乏<TTCLASS="FILENAME">/dev/fd/<n></TT>文件, Bash会使用临时文件. (感谢, S.C.)</P><P>进程替换可以比较两个不同命令的输出, 甚至能够比较同一个命令不同选项情况下的输出. </P><TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">comm <(ls -l) <(ls -al)</KBD><SAMPCLASS="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</SAMP></PRE></FONT></TD></TR></TABLE><P> 使用进程替换来比较两个不同目录的内容(可以查看哪些文件名相同, 哪些文件名不同): <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 diff <(ls $first_directory) <(ls $second_directory)</PRE></FONT></TD></TR></TABLE> </P><P>一些进程替换的其他用法与技巧: </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 cat <(ls -l) 2 # 等价于 ls -l | cat 3 4 sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin) 5 # 列出系统3个主要'bin'目录中的所有文件, 并且按文件名进行排序. 6 # 注意是3个(查一下, 上面就3个圆括号)明显不同的命令输出传递给'sort'. 7 8 9 diff <(command1) <(command2) # 给出两个命令输出的不同之处. 10 11 tar cf >(bzip2 -c > file.tar.bz2) $directory_name 12 # 调用"tar cf /dev/fd/?? $directory_name", 和"bzip2 -c > file.tar.bz2". 13 # 14 # 因为/dev/fd/<n>的系统属性, 15 # 所以两个命令之间的管道不必被命名. 16 # 17 # 这种效果可以被模拟出来. 18 # 19 bzip2 -c < pipe > file.tar.bz2& 20 tar cf pipe $directory_name 21 rm pipe 22 # 或 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 # 感谢, Stephane Chazelas</PRE></FONT></TD></TR></TABLE></P><P>一个读者给我发了一个有趣的例子, 是关于进程替换的, 如下. </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 # 摘自SuSE发行版中的代码片断: 2 3 while read des what mask iface; do 4 # 这里省略了一些命令... 5 done < <(route -n) 6 7 8 # 为了测试它, 我们让它做点事. 9 while read des what mask iface; do 10 echo $des $what $mask $iface 11 done < <(route -n) 12 13 # 输出: 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 # 就像Stephane Chazelas所给出的那样, 一个更容易理解的等价代码是: 21 route -n | 22 while read des what mask iface; do # 管道的输出被赋值给了变量. 23 echo $des $what $mask $iface 24 done # 这将产生出与上边相同的输出. 25 # 然而, Ulrich Gayer指出 . . . 26 #+ 这个简单的等价版本在while循环中使用了一个子shell, 27 #+ 因此当管道结束后, 变量就消失了. 28 29 30 31 # 更进一步, Filip Moritz解释了上面两个例子之间存在一个细微的不同之处, 32 #+ 如下所示. 33 34 ( 35 route -n | while read x; do ((y++)); done 36 echo $y # $y 仍然没有被声明或设置 37 38 while read x; do ((y++)); done < <(route -n) 39 echo $y # $y 的值为route -n的输出行数. 40 ) 41 42 # 一般来说, (译者注: 原书作者在这里并未加注释符号"#", 应该是笔误) 43 ( 44 : | x=x 45 # 看上去是启动了一个子shell 46 : | ( x=x ) 47 # 但 48 x=x < <(:) 49 # 其实不是 50 ) 51 52 # 当你要解析csv或类似东西的时侯, 这非常有用. 53 # 事实上, 这就是SuSE的这个代码片断所要实现的功能. </PRE></FONT></TD></TR></TABLE></P></DIV><H3CLASS="FOOTNOTES">注意事项</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN14284"HREF="process-sub.html#AEN14284"><SPANCLASS="footnote">[1]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>这与<AHREF="extmisc.html#NAMEDPIPEREF">命名管道</A>(临时文件)具有相同的作用, 并且, 事实上, 命名管道也被同时使用在进程替换中. </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">前一页</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html"ACCESSKEY="H">首页</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="functions.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">受限shell</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="part4.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">函数</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?