commandsub.html

来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 1,239 行 · 第 1/2 页

HTML
1,239
字号
<!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="part3.html"><LINKREL="PREVIOUS"TITLE="分析一个系统脚本"HREF="sysscripts.html"><LINKREL="NEXT"TITLE="算术扩展"HREF="arithexp.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="sysscripts.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="arithexp.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="COMMANDSUB"></A>14. 命令替换</H1><P>		   <ANAME="COMMANDSUBREF"></A><BCLASS="COMMAND">命令替换</B>能够重新分配一个<ANAME="AEN13093"HREF="#FTN.AEN13093"><SPANCLASS="footnote">[1]</SPAN></A>	     甚至是多个命令的输出; 	     它会将命令的输出如实地添加到另一个上下文中. 	        <ANAME="AEN13099"HREF="#FTN.AEN13099"><SPANCLASS="footnote">[2]</SPAN></A>	     </P><P><ANAME="BACKQUOTESREF"></A>命令替换的典型用法形式, 			是使用<ICLASS="FIRSTTERM">后置引用</I>(`...`). 			使用后置引用的(反引号)命令会产生命令行文本. 	      <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;script_name=`basename $0`  2&nbsp;echo "The name of this script is $script_name."</PRE></FONT></TD></TR></TABLE></P><DIVCLASS="FORMALPARA"><P><B>这样一来, 命令的输出就能够保存到变量中, 			或者传递到另一个命令中作为这个命令的参数, 			甚至可以用来产生<AHREF="loops1.html#FORLOOPREF1">for</A>循环的参数列表. 	      . </B></P></DIV><P>	      <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;rm `cat filename`   # <SPANCLASS="QUOTE">"filename"</SPAN>包含了需要被删除的文件列表.   2&nbsp;#  3&nbsp;# S. C. 指出, 这种使用方法可能会产生"参数列表太长"的错误.   4&nbsp;# 更好的方法是              xargs rm -- &#60; filename   5&nbsp;# ( -- 同时涵盖了某些特殊情况, 这种特殊情况就是, 以<SPANCLASS="QUOTE">"-"</SPAN>开头的文件名会产生不良结果.)  6&nbsp;  7&nbsp;textfile_listing=`ls *.txt`  8&nbsp;# 变量中包含了当前工作目录下所有的*.txt文件.   9&nbsp;echo $textfile_listing 10&nbsp; 11&nbsp;textfile_listing2=$(ls *.txt)   # 这是命令替换的另一种形式.  12&nbsp;echo $textfile_listing2 13&nbsp;# 同样的结果.  14&nbsp; 15&nbsp;# 如果将文件列表放入到一个字符串中的话,  16&nbsp;# 可能会混入一个新行.  17&nbsp;# 18&nbsp;# 一种安全的将文件列表传递到参数中的方法就是使用数组.  19&nbsp;#      shopt -s nullglob    # 如果不匹配, 那就不进行文件名扩展.  20&nbsp;#      textfile_listing=( *.txt ) 21&nbsp;# 22&nbsp;# 感谢, S.C.</PRE></FONT></TD></TR></TABLE>            </P><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>命令替换将会调用一个<AHREF="subshells.html#SUBSHELLSREF">subshell</A>. </P></TD></TR></TABLE></DIV><DIVCLASS="CAUTION"><P></P><TABLECLASS="CAUTION"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/caution.gif"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>命令替换可能会引起单词分割(word split). 	        <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;COMMAND `echo a b`     # 两个参数: a and b  2&nbsp;  3&nbsp;COMMAND "`echo a b`"   # 1个参数: "a b"  4&nbsp;  5&nbsp;COMMAND `echo`         # 无参数  6&nbsp;  7&nbsp;COMMAND "`echo`"       # 一个空参数  8&nbsp;  9&nbsp; 10&nbsp;# 感谢, S.C.</PRE></FONT></TD></TR></TABLE></P><P>即使没有引起单词分割(word split), 			  命令替换也会去掉多余的新行. 		<TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;# cd "`pwd`"  # 这句总能正常运行.   2&nbsp;# 然而...  3&nbsp;  4&nbsp;mkdir 'dir with trailing newline  5&nbsp;'  6&nbsp;  7&nbsp;cd 'dir with trailing newline  8&nbsp;'  9&nbsp; 10&nbsp;cd "`pwd`"  # 错误消息: 11&nbsp;# bash: cd: /tmp/file with trailing newline: No such file or directory 12&nbsp; 13&nbsp;cd "$PWD"   # 运行良好. 14&nbsp; 15&nbsp; 16&nbsp; 17&nbsp; 18&nbsp; 19&nbsp;old_tty_setting=$(stty -g)   # 保存旧的终端设置.  20&nbsp;echo "Hit a key " 21&nbsp;stty -icanon -echo           # 对终端禁用"canonical"模式.  22&nbsp;                             # 这样的话, 也会禁用了*本地*的echo.  23&nbsp;key=$(dd bs=1 count=1 2&#62; /dev/null)   #  使用'dd'命令来取得一个按键.  24&nbsp;stty "$old_tty_setting"      # 恢复旧的设置.  25&nbsp;echo "You hit ${#key} key."  # ${#variable} = number of characters in $variable 26&nbsp;# 27&nbsp;# 除了回车, 你随便敲任何按键都会输出"You hit 1 key." 28&nbsp;# 如果敲回车, 那么输出就是"You hit 0 key." 29&nbsp;# 新行已经被命令替换吃掉了.  30&nbsp; 31&nbsp;感谢, S.C.</PRE></FONT></TD></TR></TABLE>              </P></TD></TR></TABLE></DIV><DIVCLASS="CAUTION"><P></P><TABLECLASS="CAUTION"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/caution.gif"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>如果用<BCLASS="COMMAND">echo</B>命令输出一个<EM>未引用</EM>变量, 		而且这个变量以命令替换的结果作为值, 		那么这个变量中的换行符将会被删除. 		这可能会引起一些异常状况. 		<TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;dir_listing=`ls -l`  2&nbsp;echo $dir_listing     # 未引用, 就是没用引号括起来  3&nbsp;  4&nbsp;# 期望打印出经过排序的目录列表.   5&nbsp;  6&nbsp;# 可惜, 我们只能获得这些:   7&nbsp;# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo  8&nbsp;# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh  9&nbsp; 10&nbsp;# 新行消失了.  11&nbsp; 12&nbsp; 13&nbsp;echo "$dir_listing"   # 引用起来 14&nbsp;# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt 15&nbsp;# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh 16&nbsp;# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh</PRE></FONT></TD></TR></TABLE>              </P></TD></TR></TABLE></DIV><P>命令替换甚至允许将整个文件的内容放到变量中, 				  可以使用<AHREF="io-redirection.html#IOREDIRREF">重定向</A>或者<AHREF="basic.html#CATREF">cat</A>命令. </P><P>	         <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;variable1=`&#60;file1`      #  将"file1"的内容放到"variable1"中.   2&nbsp;variable2=`cat file2`   #  将"file2"的内容放到"variable2"中.   3&nbsp;                        #  但是这行将会fork一个新进程,   4&nbsp;                        #+ 所以这行代码将会比第一行代码执行得慢.   5&nbsp;  6&nbsp;#  注意:  7&nbsp;#  变量中可以包含空白,   8&nbsp;#+ 甚至是(厌恶至极的), 控制字符. </PRE></FONT></TD></TR></TABLE>             </P><P>	         <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#  摘录自系统文件, /etc/rc.d/rc.sysinit  2&nbsp;#+ (这是红帽系统中的)  3&nbsp;  4&nbsp;  5&nbsp;if [ -f /fsckoptions ]; then  6&nbsp;        fsckoptions=`cat /fsckoptions`  7&nbsp;...  8&nbsp;fi  9&nbsp;# 10&nbsp;# 11&nbsp;if [ -e "/proc/ide/${disk[$device]}/media" ] ; then 12&nbsp;             hdmedia=`cat /proc/ide/${disk[$device]}/media` 13&nbsp;... 14&nbsp;fi 15&nbsp;# 16&nbsp;# 17&nbsp;if [ ! -n "`uname -r | grep -- "-"`" ]; then 18&nbsp;       ktag="`cat /proc/version`" 19&nbsp;... 20&nbsp;fi 21&nbsp;# 22&nbsp;# 23&nbsp;if [ $usb = "1" ]; then 24&nbsp;    sleep 5 25&nbsp;    mouseoutput=`cat /proc/bus/usb/devices 2&#62;/dev/null|grep -E "^I.*Cls=03.*Prot=02"` 26&nbsp;    kbdoutput=`cat /proc/bus/usb/devices 2&#62;/dev/null|grep -E "^I.*Cls=03.*Prot=01"` 27&nbsp;... 28&nbsp;fi</PRE></FONT></TD></TR></TABLE>             </P><DIVCLASS="CAUTION"><P></P><TABLECLASS="CAUTION"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/caution.gif"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>不要将一个<EM>长</EM>文本文件的全部内容设置到变量中, 	       除非你有一个非常好的原因非这么做不可, 	       也不要将<EM>二进制</EM>文件的内容保存到变量中, 即使是开玩笑也不行. </P><DIVCLASS="EXAMPLE"><HR><ANAME="STUPSCR"></A><P><B>例子 14-1. 愚蠢的脚本策略</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# stupid-script-tricks.sh: 朋友, 别在家试这个脚本.   3&nbsp;# 来自于"Stupid Script Tricks," 卷I.  4&nbsp;  5&nbsp;  6&nbsp;dangerous_variable=`cat /boot/vmlinuz`   # 这是压缩过的Linux内核自身.   7&nbsp;  8&nbsp;echo "string-length of \$dangerous_variable = ${#dangerous_variable}"  9&nbsp;# 这个字符串变量的长度是$dangerous_variable = 794151 10&nbsp;# (不要使用as 'wc -c /boot/vmlinuz'来计算长度.) 11&nbsp; 12&nbsp;# echo "$dangerous_variable" 13&nbsp;# 千万别尝试这么做! 这样将挂起这个脚本.  14&nbsp; 15&nbsp; 16&nbsp;#  脚本作者已经意识到将二进制文件设置到 17&nbsp;#+ 变量中一点作用都没有.  18&nbsp; 19&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>注意, 在这里不会发生<EM>缓冲区溢出</EM>错误. 			 因为这是一个解释型语言的实例, 			 Bash就是一种解释型语言, 			 解释型语言会比编译型语言提供更多的对程序错误的保护措施. 	       </P></TD></TR></TABLE></DIV><P>变量替换允许将一个<AHREF="loops1.html#FORLOOPREF1">loop</A>的输出设置到一个变量中. 		这么做的关键就是将循环中<AHREF="internal.html#ECHOREF">echo</A>命令的输出全部截取. 		</P><DIVCLASS="EXAMPLE"><HR><ANAME="CSUBLOOP"></A><P><B>例子 14-2. 将一个循环输出的内容设置到变量中</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# csubloop.sh: 将循环输出的内容设置到变量中.   3&nbsp;  4&nbsp;variable1=`for i in 1 2 3 4 5  5&nbsp;do  6&nbsp;  echo -n "$i"                 #  对于在这里的命令替换来说  7&nbsp;done`                          #+ 这个'echo'命令是非常关键的.   8&nbsp;  9&nbsp;echo "variable1 = $variable1"  # variable1 = 12345 10&nbsp; 11&nbsp; 12&nbsp;i=0 13&nbsp;variable2=`while [ "$i" -lt 10 ] 14&nbsp;do 15&nbsp;  echo -n "$i"                 # 再来一个, 'echo'是必需的.  16&nbsp;  let "i += 1"                 # 递增.  17&nbsp;done` 18&nbsp; 19&nbsp;echo "variable2 = $variable2"  # variable2 = 0123456789 20&nbsp; 21&nbsp;#  这就证明了在一个变量的声明中 22&nbsp;#+ 嵌入一个循环是可行的.  23&nbsp; 24&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><TABLECLASS="SIDEBAR"BORDER="1"CELLPADDING="5"><TR><TD><DIVCLASS="SIDEBAR"><P></P><ANAME="AEN13150"></A

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?