gotchas.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 1,253 行 · 第 1/2 页
HTML
1,253 行
BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 echo "Here" 4 5 unix2dos $0 # 脚本先将自己改为DOS格式. 6 chmod 755 $0 # 更改可执行权限. 7 # 'unix2dos'会删除可执行权限. 8 9 ./$0 # 脚本尝试再次运行自己. 10 # 但它作为一个DOS文件, 已经不能运行了. 11 12 echo "There" 13 14 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 WHATEVER=/home/bozo 2 export WHATEVER 3 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 #!/bin/bash 2 # 子shell中的变量缺陷. 3 4 outer_variable=outer 5 echo 6 echo "outer_variable = $outer_variable" 7 echo 8 9 ( 10 # 开始子shell 11 12 echo "outer_variable inside subshell = $outer_variable" 13 inner_variable=inner # Set 14 echo "inner_variable inside subshell = $inner_variable" 15 outer_variable=inner # 会修改全局变量么? 16 echo "outer_variable inside subshell = $outer_variable" 17 18 # 如果将变量'导出'会产生不同的结果么? 19 # export inner_variable 20 # export outer_variable 21 # 试试看. 22 23 # 结束子shell 24 ) 25 26 echo 27 echo "inner_variable outside subshell = $inner_variable" # 未设置. 28 echo "outer_variable outside subshell = $outer_variable" # 未修改. 29 echo 30 31 exit 0 32 33 # 如果你打开第19和第20行的注释会怎样? 34 # 会产生不同的结果么? (译者注: 小提示, 第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 #!/bin/bash 2 # badread.sh: 3 # 尝试使用'echo'和'read'命令 4 #+ 非交互的给变量赋值. 5 6 a=aaa 7 b=bbb 8 c=ccc 9 10 echo "one two three" | read a b c 11 # 尝试重新给变量a, b, 和c赋值. 12 13 echo 14 echo "a = $a" # a = aaa 15 echo "b = $b" # b = bbb 16 echo "c = $c" # c = ccc 17 # 重新赋值失败. 18 19 # ------------------------------ 20 21 # 试试下边这种方法. 22 23 var=`echo "one two three"` 24 set -- $var 25 a=$1; b=$2; c=$3 26 27 echo "-------" 28 echo "a = $a" # a = one 29 echo "b = $b" # b = two 30 echo "c = $c" # c = three 31 # 重新赋值成功. 32 33 # ------------------------------ 34 35 # 也请注意, echo到'read'的值只会在子shell中起作用. 36 # 所以, 变量的值*只*会在子shell中被修改. 37 38 a=aaa # 重新开始. 39 b=bbb 40 c=ccc 41 42 echo; echo 43 echo "one two three" | ( read a b c; 44 echo "Inside subshell: "; echo "a = $a"; echo "b = $b"; echo "c = $c" ) 45 # a = one 46 # b = two 47 # c = three 48 echo "-----------------" 49 echo "Outside subshell: " 50 echo "a = $a" # a = aaa 51 echo "b = $b" # b = bbb 52 echo "c = $c" # c = ccc 53 echo 54 55 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 # 循环的管道问题. 2 # 这个例子由Anthony Richardson编写, 3 #+ 由Wilbert Berendsen补遗. 4 5 6 foundone=false 7 find $HOME -type f -atime +30 -size 100k | 8 while true 9 do 10 read f 11 echo "$f is over 100KB and has not been accessed in over 30 days" 12 echo "Consider moving the file to archives." 13 foundone=true 14 # ------------------------------------ 15 echo "Subshell level = $BASH_SUBSHELL" 16 # Subshell level = 1 17 # 没错, 现在是在子shell中运行. 18 # ------------------------------------ 19 done 20 21 # 变量foundone在这里肯定是false, 22 #+ 因为它是在子shell中被设置为true的. 23 if [ $foundone = false ] 24 then 25 echo "No files need archiving." 26 fi 27 28 # =====================现在, 下边是正确的方法:================= 29 30 foundone=false 31 for f in $(find $HOME -type f -atime +30 -size 100k) # 这里没使用管道. 32 do 33 echo "$f is over 100KB and has not been accessed in over 30 days" 34 echo "Consider moving the file to archives." 35 foundone=true 36 done 37 38 if [ $foundone = false ] 39 then 40 echo "No files need archiving." 41 fi 42 43 # ==================这里是另一种方法================== 44 45 # 将脚本中读取变量的部分放到一个代码块中, 46 #+ 这样一来, 它们就能在相同的子shell中共享了. 47 # 感谢, W.B. 48 49 find $HOME -type f -atime +30 -size 100k | { 50 foundone=false 51 while read f 52 do 53 echo "$f is over 100KB and has not been accessed in over 30 days" 54 echo "Consider moving the file to archives." 55 foundone=true 56 done 57 58 if ! $foundone 59 then 60 echo "No files need archiving." 61 fi 62 }</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 tail -f /var/log/messages | grep "$ERROR_MSG" >> error.log 2 # "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%"> </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%"> </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 + -
显示快捷键?