gotchas.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 1,253 行 · 第 1/2 页
HTML
1,253 行
<!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="选项"HREF="options.html"><LINKREL="NEXT"TITLE="脚本编程风格"HREF="scrstyle.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="options.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="scrstyle.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="GOTCHAS"></A>31. 陷阱</H1><TABLEBORDER="0"WIDTH="100%"CELLSPACING="0"CELLPADDING="0"CLASS="EPIGRAPH"><TR><TDWIDTH="45%"> </TD><TDWIDTH="45%"ALIGN="LEFT"VALIGN="TOP"><I><P><I>Turandot: Gli enigmi sono tre, la morte una!</I></P><P><I>Caleph: No, no! Gli enigmi sono tre, una la vita!</I></P></I></TD></TR><TR><TDWIDTH="45%"> </TD><TDWIDTH="45%"ALIGN="RIGHT"VALIGN="TOP"><I><SPANCLASS="ATTRIBUTION">Puccini</SPAN></I></TD></TR></TABLE><P><ANAME="BASH3GOTCHA"></A></P><P></P><UL><LI><P>将保留字或特殊字符声明为变量名. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 case=value0 # 引发错误. 2 23skidoo=value1 # 也会引发错误. 3 # 以数字开头的变量名是被shell保留使用的. 4 # 试试_23skidoo=value1. 以下划线开头的变量就没问题. 5 6 # 然而 . . . 如果只用一个下划线作为变量名就不行. 7 _=25 8 echo $_ # $_是一个特殊变量, 代表最后一个命令的最后一个参数. 9 10 xyz((!*=value2 # 引起严重的错误. 11 # Bash3.0之后, 标点不能出现在变量名中. </PRE></FONT></TD></TR></TABLE> </P></LI><LI><P>使用连字符或其他保留字符来做变量名(或函数名). </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 var-1=23 2 # Use 'var_1' instead. 3 4 function-whatever () # 错误 5 # 使用'function_whatever ()'来代替. 6 7 8 # Bash3.0之后, 标点不能出现在函数名中. 9 function.whatever () # 错误 10 # 使用'functionWhatever ()'来代替. </PRE></FONT></TD></TR></TABLE> </P></LI><LI><P>让变量名与函数名相同. 这会使得脚本的可读性变得很差. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 do_something () 2 { 3 echo "This function does something with \"$1\"." 4 } 5 6 do_something=do_something 7 8 do_something do_something 9 10 # 这么做是合法的, 但是会让人混淆. </PRE></FONT></TD></TR></TABLE> </P></LI><LI><P><ANAME="WSBAD"></A>不合时宜的使用<AHREF="special-chars.html#WHITESPACEREF">空白</A>字符. 与其它编程语言相比, Bash非常讲究空白字符的使用. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 var1 = 23 # 'var1=23'才是正确的. 2 # 对于上边这一行来说, Bash会把"var1"当作命令来执行, 3 # "="和"23"会被看作"命令""var1"的参数. 4 5 let c = $a - $b # 'let c=$a-$b'或'let "c = $a - $b"'才是正确的. 6 7 if [ $a -le 5] # if [ $a -le 5 ] 是正确的. 8 # if [ "$a" -le 5 ] 这么写更好. 9 # [[ $a -le 5 ]] 也行. </PRE></FONT></TD></TR></TABLE> </P></LI><LI><P>在<AHREF="special-chars.html#CODEBLOCKREF">大括号包含的代码块</A>中, 最后一条命令没有以<ICLASS="FIRSTTERM">分号</I>结尾. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 { ls -l; df; echo "Done." } 2 # bash: syntax error: unexpected end of file 3 4 { ls -l; df; echo "Done."; } 5 # ^ ### 最后的这条命令必须以分号结尾. </PRE></FONT></TD></TR></TABLE> </P></LI><LI><P> 假定未初始化的变量(赋值前的变量)被<SPANCLASS="QUOTE">"清0"</SPAN>. 事实上, 未初始化的变量值为<SPANCLASS="QUOTE">"null"</SPAN>, 而<EM>不是</EM>0. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 echo "uninitialized_var = $uninitialized_var" 4 # uninitialized_var =</PRE></FONT></TD></TR></TABLE> </P></LI><LI><P>混淆测试符号<EM>=</EM>和<EM>-eq</EM>. 请记住, <EM>=</EM>用于比较字符变量, 而<EM>-eq</EM>用来比较整数. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 if [ "$a" = 273 ] # $a是整数还是字符串? 2 if [ "$a" -eq 273 ] # $a为整数. 3 4 # 有些情况下, 即使你混用-eq和=, 也不会产生错误的结果. 5 # 然而 . . . 6 7 8 a=273.0 # 不是一个整数. 9 10 if [ "$a" = 273 ] 11 then 12 echo "Comparison works." 13 else 14 echo "Comparison does not work." 15 fi # Comparison does not work. 16 17 # 与a=" 273"和a="0273"相同. 18 19 20 # 类似的, 如果对非整数值使用"-eq"的话, 就会产生问题. 21 22 if [ "$a" -eq 273.0 ] 23 then 24 echo "a = $a" 25 fi # 因为产生了错误消息, 所以退出. 26 # test.sh: [: 273.0: integer expression expected</PRE></FONT></TD></TR></TABLE> </P></LI><LI><P>误用了<AHREF="comparison-ops.html#SCOMPARISON1">字符串比较</A>操作符. </P><DIVCLASS="EXAMPLE"><HR><ANAME="BADOP"></A><P><B>例子 31-1. 数字比较与字符串比较并不相同</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # bad-op.sh: 尝试一下对整数使用字符串比较. 3 4 echo 5 number=1 6 7 # 下面的"while循环"有两个错误: 8 #+ 一个比较明显, 而另一个比较隐蔽. 9 10 while [ "$number" < 5 ] # 错! 应该是: while [ "$number" -lt 5 ] 11 do 12 echo -n "$number " 13 let "number += 1" 14 done 15 # 如果你企图运行这个错误的脚本, 那么就会得到一个错误消息: 16 #+ bad-op.sh: line 10: 5: No such file or directory 17 # 在单中括号结构([ ])中, "<"必须被转义. 18 #+ 即便如此, 比较两个整数还是错误的. 19 20 21 echo "---------------------" 22 23 24 while [ "$number" \< 5 ] # 1 2 3 4 25 do # 26 echo -n "$number " # 看起来*好像可以工作, 但是 . . . 27 let "number += 1" #+ 事实上是比较ASCII码, 28 done #+ 而不是整数比较. 29 30 echo; echo "---------------------" 31 32 # 这么做会产生问题. 比如: 33 34 lesser=5 35 greater=105 36 37 if [ "$greater" \< "$lesser" ] 38 then 39 echo "$greater is less than $lesser" 40 fi # 105 is less than 5 41 # 事实上, 在字符串比较中(按照ASCII码的顺序) 42 #+ "105"小于"5". 43 44 echo 45 46 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV></LI><LI><P>有时候在<SPANCLASS="QUOTE">"test"</SPAN>中括号([ ])结构里的变量需要被引用起来(双引号). 如果不这么做的话, 可能会引起不可预料的结果. 请参考<AHREF="comparison-ops.html#STRTEST">例子 7-6</A>, <AHREF="redircb.html#REDIR2">例子 16-5</A>, 和<AHREF="internalvariables.html#ARGLIST">例子 9-6</A>. </P></LI><LI><P>脚本中的命令可能会因为脚本宿主不具备相应的运行权限而导致运行失败, 如果用户在命令行中就不能调用这个命令的话, 那么即使把它放到脚本中来运行, 也还是会失败. 这时可以通过修改命令的属性来解决这个问题, 有时候甚至要给它设置suid位(当然, 要以root身份来设置). </P></LI><LI><P>试图使用<BCLASS="COMMAND">-</B>作为重定向操作符(事实上它不是), 通常都会导致令人不快的结果. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 command1 2> - | command2 # 试图将command1的错误输出重定向到一个管道中... 2 # ...不会工作. 3 4 command1 2>& - | command2 # 也没效果. 5 6 感谢, S.C.</PRE></FONT></TD></TR></TABLE></P></LI><LI><P>使用Bash <AHREF="bashver2.html#BASH2REF">2.0</A>或更高版本的功能, 可以在产生错误信息的时候, 引发修复动作. 但是比较老的Linux机器默认安装的可能是Bash 1.XX. </P><P> <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 minimum_version=2 4 # 因为Chet Ramey经常给Bash添加一些新的特征, 5 # 所以你最好将$minimum_version设置为2.XX, 3.XX, 或是其他你认为比较合适的值. 6 E_BAD_VERSION=80 7 8 if [ "$BASH_VERSION" \< "$minimum_version" ] 9 then 10 echo "This script works only with Bash, version $minimum or greater." 11 echo "Upgrade strongly recommended." 12 exit $E_BAD_VERSION 13 fi 14 15 ...</PRE></FONT></TD></TR></TABLE></P></LI><LI><P>在非Linux机器上的Bourne shell脚本(<KBDCLASS="USERINPUT">#!/bin/sh</KBD>)中使用Bash特有的功能, 可能会引起不可预料的行为. Linux系统通常都会把<BCLASS="COMMAND">bash</B>别名化为<BCLASS="COMMAND">sh</B>, 但是在一般的UNIX机器上却不一定会这么做. </P></LI><LI><P>使用Bash未文档化的特征, 将是一种危险的举动. 本书之前的几个版本就依赖一个这种<SPANCLASS="QUOTE">"特征"</SPAN>, 下面说明一下这个<SPANCLASS="QUOTE">"特征"</SPAN>, 虽然<AHREF="exit-status.html#EXITSTATUSREF">exit</A>或<AHREF="complexfunct.html#RETURNREF">return</A>所能返回的最大正值为255, 但是并没有限制我们使用<EM>负</EM>整数. 不幸的是, Bash 2.05b之后的版本, 这个漏洞消失了. 请参考<AHREF="complexfunct.html#RETURNTEST">例子 23-9</A>. </P></LI><LI><P> 一个带有DOS风格换行符(<TTCLASS="REPLACEABLE"><I>\r\n</I></TT>)的脚本将会运行失败, 因为<KBDCLASS="USERINPUT">#!/bin/bash\r\n</KBD>是<EM>不</EM>合法的, 与我们所期望的<KBDCLASS="USERINPUT">#!/bin/bash\n</KBD><EM>不同</EM>. 解决办法就是将这个脚本转换为UNIX风格的换行符. </P><P> <TABLEBORDER="0"
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?