redircb.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 600 行
HTML
600 行
<!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="I/O重定向"HREF="io-redirection.html"><LINKREL="PREVIOUS"TITLE="使用exec"HREF="x13380.html"><LINKREL="NEXT"TITLE="重定向的应用"HREF="redirapps.html"></HEAD><BODYCLASS="SECT1"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="x13380.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">16. I/O重定向</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="redirapps.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="REDIRCB">16.2. 代码块重定向</A></H1><P><ANAME="REDIRREF"></A>象<AHREF="loops1.html#WHILELOOPREF">while</A>, <AHREF="loops1.html#UNTILLOOPREF">until</A>, 和<AHREF="loops1.html#FORLOOPREF1">for</A>循环代码块, 甚至<AHREF="tests.html#IFTHEN">if/then</A>测试结构的代码块, 都可以对<TTCLASS="FILENAME">stdin</TT>进行重定向. 即使函数也可以使用这种重定向方式(请参考<AHREF="complexfunct.html#REALNAME">例子 23-11</A>). 要想做到这些, 都要依靠代码块结尾的<SPANCLASS="TOKEN"><</SPAN>操作符. </P><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR2"></A><P><B>例子 16-5. <EM>while</EM>循环的重定向</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # redir2.sh 3 4 if [ -z "$1" ] 5 then 6 Filename=names.data # 如果没有指定文件名, 则使用这个默认值. 7 else 8 Filename=$1 9 fi 10 #+ Filename=${1:-names.data} 11 # 这句可代替上面的测试(参数替换). 12 13 count=0 14 15 echo 16 17 while [ "$name" != Smith ] # 为什么变量$name要用引号? 18 do 19 read name # 从$Filename文件中读取输入, 而不是在stdin中读取输入. 20 echo $name 21 let "count += 1" 22 done <"$Filename" # 重定向stdin到文件$Filename. 23 # ^^^^^^^^^^^^ 24 25 echo; echo "$count names read"; echo 26 27 exit 0 28 29 # 注意在一些比较老的shell脚本编程语言中, 30 #+ 重定向的循环是放在子shell里运行的. 31 # 因此, $count 值返回后会是 0, 此值是在循环开始前的初始值. 32 # *如果可能的话*, 尽量避免在Bash或ksh中使用子shell, 33 #+ 所以这个脚本能够正确的运行. 34 # (多谢Heiner Steven指出这个问题.) 35 36 # 然而 . . . 37 # Bash有时还是*会*在一个使用管道的"while-read"循环中启动一个子shell, 38 #+ 与重定向的"while"循环还是有区别的. 39 40 abc=hi 41 echo -e "1\n2\n3" | while read l 42 do abc="$l" 43 echo $abc 44 done 45 echo $abc 46 47 # 感谢, Bruno de Oliveira Schneider 48 #+ 给出上面的代码片段来演示此问题. 49 # 同时, 感谢, Brian Onn, 修正了一个注释错误. </PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR2A"></A><P><B>例子 16-6. 重定向<EM>while</EM>循环的另一种形式</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # 这是上个脚本的另一个版本. 4 5 # Heiner Steven建议, 6 #+ 为了避免重定向循环运行在子shell中(老版本的shell会这么做), 最好让重定向循环运行在当前工作区内, 7 #+ 这样的话, 需要提前进行文件描述符重定向, 8 #+ 因为变量如果在(子shell上运行的)循环中被修改的话, 循环结束后并不会保存修改后的值. 9 10 11 if [ -z "$1" ] 12 then 13 Filename=names.data # 如果没有指定文件名则使用默认值. 14 else 15 Filename=$1 16 fi 17 18 19 exec 3<&0 # 将stdin保存到文件描述符3. 20 exec 0<"$Filename" # 重定向标准输入. 21 22 count=0 23 echo 24 25 26 while [ "$name" != Smith ] 27 do 28 read name # 从stdin(现在已经是$Filename了)中读取. 29 echo $name 30 let "count += 1" 31 done # 从文件$Filename中循环读取 32 #+ 因为文件(译者注:指默认文件, 在本节最后)有20行. 33 34 # 这个脚本原先在"while"循环的结尾还有一句: 35 #+ done <"$Filename" 36 # 练习: 37 # 为什么不需要这句了? 38 39 40 exec 0<&3 # 恢复保存的stdin. 41 exec 3<&- # 关闭临时文件描述符3. 42 43 echo; echo "$count names read"; echo 44 45 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR3"></A><P><B>例子 16-7. 重定向<EM>until</EM>循环</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 和前面的例子相同, 但使用的是"until"循环. 3 4 if [ -z "$1" ] 5 then 6 Filename=names.data # 如果没有指定文件名那就使用默认值. 7 else 8 Filename=$1 9 fi 10 11 # while [ "$name" != Smith ] 12 until [ "$name" = Smith ] # 把!=改为=. 13 do 14 read name # 从$Filename中读取, 而不是从stdin中读取. 15 echo $name 16 done <"$Filename" # 重定向stdin到文件$Filename. 17 # ^^^^^^^^^^^^ 18 19 # 结果和前面例子的"while"循环相同. 20 21 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR4"></A><P><B>例子 16-8. 重定向<EM>for</EM>循环</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果没有指定文件名就使用默认值. 6 else 7 Filename=$1 8 fi 9 10 line_count=`wc $Filename | awk '{ print $1 }'` 11 # 目标文件的行数. 12 # 13 # 此处的代码太过做作, 并且写得很难看, 14 #+ 但至少展示了"for"循环的stdin可以重定向... 15 #+ 当然, 你得足够聪明, 才能看得出来. 16 # 17 # 更简洁的写法是 line_count=$(wc -l < "$Filename") 18 19 20 for name in `seq $line_count` # "seq"打印出数字序列. 21 # while [ "$name" != Smith ] -- 比"while"循环更复杂 -- 22 do 23 read name # 从$Filename中, 而非从stdin中读取. 24 echo $name 25 if [ "$name" = Smith ] # 因为用for循环, 所以需要这个多余测试. 26 then 27 break 28 fi 29 done <"$Filename" # 重定向stdin到文件$Filename. 30 # ^^^^^^^^^^^^ 31 32 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>我们也可以修改前面的例子使其能重定向循环的标准输出. </P><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR4A"></A><P><B>例子 16-9. 重定向<EM>for</EM>循环(<TTCLASS="FILENAME">stdin</TT>和<TTCLASS="FILENAME">stdout</TT>都进行重定向)</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果没有指定文件名, 则使用默认值. 6 else 7 Filename=$1 8 fi 9 10 Savefile=$Filename.new # 保存最终结果的文件名. 11 FinalName=Jonah # 终止"read"时的名称. 12 13 line_count=`wc $Filename | awk '{ print $1 }'` # 目标文件的行数. 14 15 16 for name in `seq $line_count` 17 do 18 read name 19 echo "$name" 20 if [ "$name" = "$FinalName" ] 21 then 22 break 23 fi 24 done < "$Filename" > "$Savefile" # 重定向stdin到文件$Filename, 25 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 并且将它保存到备份文件中. 26 27 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="REDIR5"></A><P><B>例子 16-10. 重定向<EM>if/then</EM>测试结构</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 if [ -z "$1" ] 4 then 5 Filename=names.data # 如果文件名没有指定, 使用默认值. 6 else 7 Filename=$1 8 fi 9 10 TRUE=1 11 12 if [ "$TRUE" ] # if true 和 if : 都可以. 13 then 14 read name 15 echo $name 16 fi <"$Filename" 17 # ^^^^^^^^^^^^ 18 19 # 只读取了文件的第一行. 20 # An "if/then"测试结构不能自动地反复地执行, 除非把它们嵌到循环里. 21 22 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="NAMESDATA"></A><P><B>例子 16-11. 用于上面例子的<SPANCLASS="QUOTE">"names.data"</SPAN>数据文件</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 Aristotle 2 Belisarius 3 Capablanca 4 Euler 5 Goethe 6 Hamurabi 7 Jonah 8 Laplace 9 Maroczy 10 Purcell 11 Schmidt 12 Semmelweiss 13 Smith 14 Turing 15 Venn 16 Wilson 17 Znosko-Borowski 18 19 # 此数据文件用于: 20 #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh".</PRE></FONT></TD></TR></TABLE><HR></DIV><P>重定向代码块的<TTCLASS="FILENAME">stdout</TT>, 与"将代码块的输出保存到文件中"具有相同的效果. 请参考<AHREF="special-chars.html#RPMCHECK">例子 3-2</A>. </P><P><AHREF="here-docs.html#HEREDOCREF">here document</A> 是重定向代码块的一个特例. </P></DIV><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="x13380.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="redirapps.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">使用<BCLASS="COMMAND">exec</B></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="io-redirection.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">重定向的应用</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?