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 script_name=`basename $0` 2 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 rm `cat filename` # <SPANCLASS="QUOTE">"filename"</SPAN>包含了需要被删除的文件列表. 2 # 3 # S. C. 指出, 这种使用方法可能会产生"参数列表太长"的错误. 4 # 更好的方法是 xargs rm -- < filename 5 # ( -- 同时涵盖了某些特殊情况, 这种特殊情况就是, 以<SPANCLASS="QUOTE">"-"</SPAN>开头的文件名会产生不良结果.) 6 7 textfile_listing=`ls *.txt` 8 # 变量中包含了当前工作目录下所有的*.txt文件. 9 echo $textfile_listing 10 11 textfile_listing2=$(ls *.txt) # 这是命令替换的另一种形式. 12 echo $textfile_listing2 13 # 同样的结果. 14 15 # 如果将文件列表放入到一个字符串中的话, 16 # 可能会混入一个新行. 17 # 18 # 一种安全的将文件列表传递到参数中的方法就是使用数组. 19 # shopt -s nullglob # 如果不匹配, 那就不进行文件名扩展. 20 # textfile_listing=( *.txt ) 21 # 22 # 感谢, 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 COMMAND `echo a b` # 两个参数: a and b 2 3 COMMAND "`echo a b`" # 1个参数: "a b" 4 5 COMMAND `echo` # 无参数 6 7 COMMAND "`echo`" # 一个空参数 8 9 10 # 感谢, S.C.</PRE></FONT></TD></TR></TABLE></P><P>即使没有引起单词分割(word split), 命令替换也会去掉多余的新行. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 # cd "`pwd`" # 这句总能正常运行. 2 # 然而... 3 4 mkdir 'dir with trailing newline 5 ' 6 7 cd 'dir with trailing newline 8 ' 9 10 cd "`pwd`" # 错误消息: 11 # bash: cd: /tmp/file with trailing newline: No such file or directory 12 13 cd "$PWD" # 运行良好. 14 15 16 17 18 19 old_tty_setting=$(stty -g) # 保存旧的终端设置. 20 echo "Hit a key " 21 stty -icanon -echo # 对终端禁用"canonical"模式. 22 # 这样的话, 也会禁用了*本地*的echo. 23 key=$(dd bs=1 count=1 2> /dev/null) # 使用'dd'命令来取得一个按键. 24 stty "$old_tty_setting" # 恢复旧的设置. 25 echo "You hit ${#key} key." # ${#variable} = number of characters in $variable 26 # 27 # 除了回车, 你随便敲任何按键都会输出"You hit 1 key." 28 # 如果敲回车, 那么输出就是"You hit 0 key." 29 # 新行已经被命令替换吃掉了. 30 31 感谢, 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 dir_listing=`ls -l` 2 echo $dir_listing # 未引用, 就是没用引号括起来 3 4 # 期望打印出经过排序的目录列表. 5 6 # 可惜, 我们只能获得这些: 7 # total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo 8 # bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh 9 10 # 新行消失了. 11 12 13 echo "$dir_listing" # 引用起来 14 # -rw-rw-r-- 1 bozo 30 May 13 17:15 1.txt 15 # -rw-rw-r-- 1 bozo 51 May 15 20:57 t2.sh 16 # -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 variable1=`<file1` # 将"file1"的内容放到"variable1"中. 2 variable2=`cat file2` # 将"file2"的内容放到"variable2"中. 3 # 但是这行将会fork一个新进程, 4 #+ 所以这行代码将会比第一行代码执行得慢. 5 6 # 注意: 7 # 变量中可以包含空白, 8 #+ 甚至是(厌恶至极的), 控制字符. </PRE></FONT></TD></TR></TABLE> </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 # 摘录自系统文件, /etc/rc.d/rc.sysinit 2 #+ (这是红帽系统中的) 3 4 5 if [ -f /fsckoptions ]; then 6 fsckoptions=`cat /fsckoptions` 7 ... 8 fi 9 # 10 # 11 if [ -e "/proc/ide/${disk[$device]}/media" ] ; then 12 hdmedia=`cat /proc/ide/${disk[$device]}/media` 13 ... 14 fi 15 # 16 # 17 if [ ! -n "`uname -r | grep -- "-"`" ]; then 18 ktag="`cat /proc/version`" 19 ... 20 fi 21 # 22 # 23 if [ $usb = "1" ]; then 24 sleep 5 25 mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"` 26 kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"` 27 ... 28 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 #!/bin/bash 2 # stupid-script-tricks.sh: 朋友, 别在家试这个脚本. 3 # 来自于"Stupid Script Tricks," 卷I. 4 5 6 dangerous_variable=`cat /boot/vmlinuz` # 这是压缩过的Linux内核自身. 7 8 echo "string-length of \$dangerous_variable = ${#dangerous_variable}" 9 # 这个字符串变量的长度是$dangerous_variable = 794151 10 # (不要使用as 'wc -c /boot/vmlinuz'来计算长度.) 11 12 # echo "$dangerous_variable" 13 # 千万别尝试这么做! 这样将挂起这个脚本. 14 15 16 # 脚本作者已经意识到将二进制文件设置到 17 #+ 变量中一点作用都没有. 18 19 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 #!/bin/bash 2 # csubloop.sh: 将循环输出的内容设置到变量中. 3 4 variable1=`for i in 1 2 3 4 5 5 do 6 echo -n "$i" # 对于在这里的命令替换来说 7 done` #+ 这个'echo'命令是非常关键的. 8 9 echo "variable1 = $variable1" # variable1 = 12345 10 11 12 i=0 13 variable2=`while [ "$i" -lt 10 ] 14 do 15 echo -n "$i" # 再来一个, 'echo'是必需的. 16 let "i += 1" # 递增. 17 done` 18 19 echo "variable2 = $variable2" # variable2 = 0123456789 20 21 # 这就证明了在一个变量的声明中 22 #+ 嵌入一个循环是可行的. 23 24 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 + -
显示快捷键?