gotchas.html

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

HTML
1,253
字号
BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;  3&nbsp;echo "Here"  4&nbsp;  5&nbsp;unix2dos $0    # 脚本先将自己改为DOS格式.   6&nbsp;chmod 755 $0   # 更改可执行权限.   7&nbsp;               # 'unix2dos'会删除可执行权限.   8&nbsp;  9&nbsp;./$0           # 脚本尝试再次运行自己.  10&nbsp;               # 但它作为一个DOS文件, 已经不能运行了.  11&nbsp; 12&nbsp;echo "There" 13&nbsp; 14&nbsp;exit 0</PRE></FONT></TD></TR></TABLE>      </P></LI><LI><P>以<KBDCLASS="USERINPUT">#!/bin/sh</KBD>开头的Bash脚本, 		  不能在完整的Bash兼容模式下运行. 		  某些Bash特定的功能可能会被禁用. 		  如果脚本需要完整的访问所有Bash专有扩展, 		  那么它需要使用<KBDCLASS="USERINPUT">#!/bin/bash</KBD>作为开头. </P></LI><LI><P>如果在<AHREF="here-docs.html#HEREDOCREF">here document</A>中, 		<AHREF="here-docs.html#INDENTEDLS">结尾的limit string之前加上空白字符</A>的话, 	将会导致脚本的异常行为. 	</P></LI><LI><P><ANAME="PARCHILDPROBREF"></A></P><P>脚本不能将变量<BCLASS="COMMAND">export</B>到它的<AHREF="internal.html#FORKREF">父进程</A>(即调用这个脚本的shell), 			  或父进程的环境中. 			  就好比我们在生物学中所学到的那样, 			  子进程只会继承父进程, 反过来则不行. 		</P><P>	  <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;WHATEVER=/home/bozo  2&nbsp;export WHATEVER  3&nbsp;exit 0</PRE></FONT></TD></TR></TABLE>          <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><BCLASS="COMMAND">echo $WHATEVER</B><SAMPCLASS="COMPUTEROUTPUT"></SAMP><SAMPCLASS="PROMPT">bash$ </SAMP></PRE></FONT></TD></TR></TABLE>      </P><P>        可以确定的是, 即使回到命令行提示符, 变量$WHATEVER仍然没有被设置.        </P></LI><LI><P>在<AHREF="subshells.html#SUBSHELLSREF">子shell</A>中设置和操作变量之后, 			如果尝试在子shell作用域之外使用同名变量的话, 将会产生令人不快的结果. 		</P><DIVCLASS="EXAMPLE"><HR><ANAME="SUBPIT"></A><P><B>例子 31-2. 子shell缺陷</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# 子shell中的变量缺陷.   3&nbsp;  4&nbsp;outer_variable=outer  5&nbsp;echo  6&nbsp;echo "outer_variable = $outer_variable"  7&nbsp;echo  8&nbsp;  9&nbsp;( 10&nbsp;# 开始子shell 11&nbsp; 12&nbsp;echo "outer_variable inside subshell = $outer_variable" 13&nbsp;inner_variable=inner  # Set 14&nbsp;echo "inner_variable inside subshell = $inner_variable" 15&nbsp;outer_variable=inner  # 会修改全局变量么?  16&nbsp;echo "outer_variable inside subshell = $outer_variable" 17&nbsp; 18&nbsp;# 如果将变量'导出'会产生不同的结果么?  19&nbsp;#    export inner_variable 20&nbsp;#    export outer_variable 21&nbsp;# 试试看.  22&nbsp; 23&nbsp;# 结束子shell 24&nbsp;) 25&nbsp; 26&nbsp;echo 27&nbsp;echo "inner_variable outside subshell = $inner_variable"  # 未设置.  28&nbsp;echo "outer_variable outside subshell = $outer_variable"  # 未修改.  29&nbsp;echo 30&nbsp; 31&nbsp;exit 0 32&nbsp; 33&nbsp;# 如果你打开第19和第20行的注释会怎样?  34&nbsp;# 会产生不同的结果么? (译者注: 小提示, 第18行的'导出'都加上引号了.)</PRE></FONT></TD></TR></TABLE><HR></DIV></LI><LI><P><ANAME="BADREAD0"></A></P><P>将<BCLASS="COMMAND">echo</B>的输出通过<AHREF="special-chars.html#PIPEREF">管道</A>传递给<AHREF="internal.html#READREF">read</A>命令可能会产生不可预料的结果. 	在这种情况下, <BCLASS="COMMAND">read</B>命令的行为就好像它在子shell中运行一样. 	可以使用<AHREF="internal.html#SETREF">set</A>命令来代替(就好像<AHREF="internal.html#SETPOS">例子 11-17</A>一样). </P><DIVCLASS="EXAMPLE"><HR><ANAME="BADREAD"></A><P><B>例子 31-3. 将<BCLASS="COMMAND">echo</B>的输出通过管道传递给<BCLASS="COMMAND">read</B>命令</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;#  badread.sh:  3&nbsp;#  尝试使用'echo'和'read'命令  4&nbsp;#+ 非交互的给变量赋值.   5&nbsp;  6&nbsp;a=aaa  7&nbsp;b=bbb  8&nbsp;c=ccc  9&nbsp; 10&nbsp;echo "one two three" | read a b c 11&nbsp;# 尝试重新给变量a, b, 和c赋值. 12&nbsp; 13&nbsp;echo 14&nbsp;echo "a = $a"  # a = aaa 15&nbsp;echo "b = $b"  # b = bbb 16&nbsp;echo "c = $c"  # c = ccc 17&nbsp;# 重新赋值失败.  18&nbsp; 19&nbsp;# ------------------------------ 20&nbsp; 21&nbsp;# 试试下边这种方法.  22&nbsp; 23&nbsp;var=`echo "one two three"` 24&nbsp;set -- $var 25&nbsp;a=$1; b=$2; c=$3 26&nbsp; 27&nbsp;echo "-------" 28&nbsp;echo "a = $a"  # a = one 29&nbsp;echo "b = $b"  # b = two 30&nbsp;echo "c = $c"  # c = three  31&nbsp;# 重新赋值成功.  32&nbsp; 33&nbsp;# ------------------------------ 34&nbsp; 35&nbsp;#  也请注意, echo到'read'的值只会在子shell中起作用.  36&nbsp;#  所以, 变量的值*只*会在子shell中被修改.  37&nbsp; 38&nbsp;a=aaa          # 重新开始.  39&nbsp;b=bbb 40&nbsp;c=ccc 41&nbsp; 42&nbsp;echo; echo 43&nbsp;echo "one two three" | ( read a b c; 44&nbsp;echo "Inside subshell: "; echo "a = $a"; echo "b = $b"; echo "c = $c" ) 45&nbsp;# a = one 46&nbsp;# b = two 47&nbsp;# c = three 48&nbsp;echo "-----------------" 49&nbsp;echo "Outside subshell: " 50&nbsp;echo "a = $a"  # a = aaa 51&nbsp;echo "b = $b"  # b = bbb 52&nbsp;echo "c = $c"  # c = ccc 53&nbsp;echo 54&nbsp; 55&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>事实上, 也正如Anthony Richardson指出的那样, 		  通过管道将输出传递到<EM>任何</EM>循环中, 都会引起类似的问题. </P><P>	<TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;# 循环的管道问题.   2&nbsp;#  这个例子由Anthony Richardson编写,   3&nbsp;#+ 由Wilbert Berendsen补遗.   4&nbsp;  5&nbsp;  6&nbsp;foundone=false  7&nbsp;find $HOME -type f -atime +30 -size 100k |  8&nbsp;while true  9&nbsp;do 10&nbsp;   read f 11&nbsp;   echo "$f is over 100KB and has not been accessed in over 30 days" 12&nbsp;   echo "Consider moving the file to archives." 13&nbsp;   foundone=true 14&nbsp;   # ------------------------------------ 15&nbsp;   echo "Subshell level = $BASH_SUBSHELL" 16&nbsp;   # Subshell level = 1 17&nbsp;   # 没错, 现在是在子shell中运行.  18&nbsp;   # ------------------------------------ 19&nbsp;done 20&nbsp;    21&nbsp;#  变量foundone在这里肯定是false,  22&nbsp;#+ 因为它是在子shell中被设置为true的.  23&nbsp;if [ $foundone = false ] 24&nbsp;then 25&nbsp;   echo "No files need archiving." 26&nbsp;fi 27&nbsp; 28&nbsp;# =====================现在, 下边是正确的方法:================= 29&nbsp; 30&nbsp;foundone=false 31&nbsp;for f in $(find $HOME -type f -atime +30 -size 100k)  # 这里没使用管道.  32&nbsp;do 33&nbsp;   echo "$f is over 100KB and has not been accessed in over 30 days" 34&nbsp;   echo "Consider moving the file to archives." 35&nbsp;   foundone=true 36&nbsp;done 37&nbsp;    38&nbsp;if [ $foundone = false ] 39&nbsp;then 40&nbsp;   echo "No files need archiving." 41&nbsp;fi 42&nbsp; 43&nbsp;# ==================这里是另一种方法================== 44&nbsp; 45&nbsp;#  将脚本中读取变量的部分放到一个代码块中,  46&nbsp;#+ 这样一来, 它们就能在相同的子shell中共享了.  47&nbsp;#  感谢, W.B. 48&nbsp; 49&nbsp;find $HOME -type f -atime +30 -size 100k | { 50&nbsp;     foundone=false 51&nbsp;     while read f 52&nbsp;     do 53&nbsp;       echo "$f is over 100KB and has not been accessed in over 30 days" 54&nbsp;       echo "Consider moving the file to archives." 55&nbsp;       foundone=true 56&nbsp;     done 57&nbsp; 58&nbsp;     if ! $foundone 59&nbsp;     then 60&nbsp;       echo "No files need archiving." 61&nbsp;     fi 62&nbsp;}</PRE></FONT></TD></TR></TABLE>      </P><P>        一个相关的问题: 当你尝试将<BCLASS="COMMAND">tail -f</B>的<TTCLASS="FILENAME">stdout</TT>通过管道传递给<AHREF="textproc.html#GREPREF">grep</A>时, 会产生问题. 	  <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;tail -f /var/log/messages | grep "$ERROR_MSG" &#62;&#62; error.log  2&nbsp;# "error.log"文件将不会写入任何东西. </PRE></FONT></TD></TR></TABLE>      </P></LI><LI><P>在脚本中使用<SPANCLASS="QUOTE">"suid"</SPAN>命令是非常危险的, 	因为这会危及系统安全. 	  <ANAME="AEN15516"HREF="#FTN.AEN15516"><SPANCLASS="footnote">[1]</SPAN></A>      </P></LI><LI><P>使用shell脚本来编写CGI程序是值得商榷的. 			因为Shell脚本的变量不是<SPANCLASS="QUOTE">"类型安全"</SPAN>的, 		当CGI被关联的时候, 可能会产生令人不快的行为. 	此外, 它还很难抵挡住<SPANCLASS="QUOTE">"破解的考验"</SPAN>. </P></LI><LI><P>Bash不能正确的处理<AHREF="internal.html#DOUBLESLASHREF">双斜线(<SPANCLASS="TOKEN">//</SPAN>)字符串</A>. </P></LI><LI><P>在Linux或BSD上编写的Bash脚本, 可能需要修改一下, 			才能使它们运行在商业的UNIX(或Apple OSX)机器上.  	这些脚本通常都使用GNU命令和过滤工具, GNU工具通常都比一般的UNIX上的同类工具更加强大. 	这方面的一个非常明显的例子就是, 文本处理工具<AHREF="textproc.html#TRREF">tr</A>. </P></LI></UL><TABLEBORDER="0"WIDTH="100%"CELLSPACING="0"CELLPADDING="0"CLASS="EPIGRAPH"><TR><TDWIDTH="45%">&nbsp;</TD><TDWIDTH="45%"ALIGN="LEFT"VALIGN="TOP"><I><P><I>危险正在接近你 --</I></P><P><I>小心, 小心, 小心, 小心.</I></P><P><I>许多勇敢的心都在沉睡.</I></P><P><I>所以一定要小心 --</I></P><P><I>小心.</I></P></I></TD></TR><TR><TDWIDTH="45%">&nbsp;</TD><TDWIDTH="45%"ALIGN="RIGHT"VALIGN="TOP"><I><SPANCLASS="ATTRIBUTION">A.J. Lamb and H.W. Petrie</SPAN></I></TD></TR></TABLE></DIV><H3CLASS="FOOTNOTES">注意事项</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN15516"HREF="gotchas.html#AEN15516"><SPANCLASS="footnote">[1]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>给脚本设置<EM>suid</EM>权限是没用的. 	    </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="options.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="scrstyle.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">选项</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 + -
显示快捷键?