othertypesv.html
来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 853 行
HTML
853 行
<!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="variables.html"><LINKREL="PREVIOUS"TITLE="Bash变量是不区分类型的"HREF="untyped.html"><LINKREL="NEXT"TITLE="引用"HREF="quoting.html"></HEAD><BODYCLASS="SECT1"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="untyped.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">4. 变量和参数的介绍</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="quoting.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="OTHERTYPESV">4.4. 特殊的变量类型</A></H1><P></P><DIVCLASS="VARIABLELIST"><DL><DT><TTCLASS="REPLACEABLE"><I>局部变量</I></TT></DT><DD><P>这种变量只有在<AHREF="special-chars.html#CODEBLOCKREF">代码块</A>或者函数中(参见<AHREF="functions.html#FUNCTIONREF">函数</A>中的<AHREF="localvar.html#LOCALREF">局部变量</A>)才可见. </P></DD><DT><ANAME="ENVREF"></A><TTCLASS="REPLACEABLE"><I>环境变量</I></TT></DT><DD><P>这种变量将影响用户接口和shell的行为</P><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>在通常情况下, 每个进程都有自己的<SPANCLASS="QUOTE">"环境"</SPAN>, 这个环境是由一组变量组成的, 这些变量中存有进程可能需要引用的信息. 在这种情况下, shell与一个一般的进程没什么区别. </P><P>每次当一个shell启动时, 它都将创建适合于自己环境变量的shell变量. 更新或者添加一个新的环境变量的话, 这个shell都会立刻更新它自己的环境(译者注: 换句话说, 更改或增加的变量会立即生效), 并且所有的shell子进程(即这个shell所执行的命令)都会继承这个环境. (译者注: 准确地说, 应该是后继生成的子进程才会继承Shell的新环境变量, 已经运行的子进程并不会得到它的新环境变量). </P></TD></TR></TABLE></DIV><DIVCLASS="CAUTION"><P></P><TABLECLASS="CAUTION"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/caution.gif"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>分配给环境变量的空间是有限的. 创建太多环境变量, 或者给一个环境变量分配太多的空间都会引起错误. </P><P> <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"</KBD><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">du</KBD><SAMPCLASS="COMPUTEROUTPUT">bash: /usr/bin/du: Argument list too long</SAMP> </PRE></FONT></TD></TR></TABLE> </P><P>(感谢 Stephane Chazelas 对这个问题的澄清, 并且提供了上边的例子程序.)</P></TD></TR></TABLE></DIV><P>如果一个脚本要设置一个环境变量, 那么需要将这些变量<SPANCLASS="QUOTE">"export"</SPAN>出来, 也就是需要通知到脚本本地的环境. 这是<AHREF="internal.html#EXPORTREF">export</A>命令的功能. </P><ANAME="CHILDREF"></A><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>一个脚本只能够<BCLASS="COMMAND">export</B>变量到这个脚本所产生的子进程, 也就是说只能够对这个脚本所产生的命令和进程起作用. 如果脚本是从命令行中调用的, 那么这个脚本所export的变量是<TTCLASS="REPLACEABLE"><I>不能</I></TT>影响命令行环境的. 也就是说, <EM><AHREF="internal.html#FORKREF">子进程</A>是不能够export变量来影响产生自己的父进程的环境的. </EM></P></TD></TR></TABLE></DIV><P>---</P></DD><DT><ANAME="POSPARAMREF1"></A><TTCLASS="REPLACEABLE"><I>位置参数</I></TT></DT><DD><P>从命令行传递到脚本的参数: <CODECLASS="VARNAME">$0</CODE>, <CODECLASS="VARNAME">$1</CODE>, <CODECLASS="VARNAME">$2</CODE>, <CODECLASS="VARNAME">$3</CODE> . . .</P><P><CODECLASS="VARNAME">$0</CODE>就是脚本文件自身的名字, <CODECLASS="VARNAME">$1</CODE> 是第一个参数, <CODECLASS="VARNAME">$2</CODE>是第二个参数, <CODECLASS="VARNAME">$3</CODE>是第三个参数, 然后是第四个. <ANAME="AEN1957"HREF="#FTN.AEN1957"><SPANCLASS="footnote">[1]</SPAN></A> <ANAME="BRACKETNOTATION"></A> <CODECLASS="VARNAME">$9</CODE>之后的位置参数就必须用大括号括起来了, 比如, <CODECLASS="VARNAME">${10}</CODE>, <CODECLASS="VARNAME">${11}</CODE>, <CODECLASS="VARNAME">${12}</CODE>. </P><P>两个比较特殊的变量<AHREF="internalvariables.html#APPREF">$*和$@</A> 表示<EM>所有的</EM>位置参数. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX17"></A><P><B>例子 4-5. 位置参数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # 作为用例, 调用这个脚本至少需要10个参数, 比如: 4 # ./scriptname 1 2 3 4 5 6 7 8 9 10 5 MINPARAMS=10 6 7 echo 8 9 echo "The name of this script is \"$0\"." 10 # 添加./是表示当前目录 11 echo "The name of this script is \"`basename $0`\"." 12 # 去掉路径名, 剩下文件名, (参见'basename') 13 14 echo 15 16 if [ -n "$1" ] # 测试变量被引用. 17 then 18 echo "Parameter #1 is $1" # 需要引用才能够转义"#" 19 fi 20 21 if [ -n "$2" ] 22 then 23 echo "Parameter #2 is $2" 24 fi 25 26 if [ -n "$3" ] 27 then 28 echo "Parameter #3 is $3" 29 fi 30 31 # ... 32 33 34 if [ -n "${10}" ] # 大于$9的参数必须用{}括起来. 35 then 36 echo "Parameter #10 is ${10}" 37 fi 38 39 echo "-----------------------------------" 40 echo "All the command-line parameters are: "$*"" 41 42 if [ $# -lt "$MINPARAMS" ] 43 then 44 echo 45 echo "This script needs at least $MINPARAMS command-line arguments!" 46 fi 47 48 echo 49 50 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P><ICLASS="FIRSTTERM">{}标记法</I>提供了一种提取从命令行传递到脚本的<EM>最后</EM>一个位置参数的简单办法. 但是这种方法同时还需要使用<AHREF="bashver2.html#VARREFNEW">间接引用</A>.</P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 args=$# # 位置参数的个数. 2 lastarg=${!args} 3 # 或: lastarg=${!#} 4 # (感谢, Chris Monson.) 5 # 注意, 不能直接使用 lastarg=${!$#} , 这会产生错误.</PRE></FONT></TD></TR></TABLE></P><P>一些脚本可能会依赖于使用不同的调用名字, 来表现出不同的行为. 如果想要达到这种目的, 一般都需要在脚本中检查<CODECLASS="VARNAME">$0</CODE>. 因为脚本只能够有一个真正的文件名, 如果要产生多个名字, 必须使用符号链接. 参见<AHREF="basic.html#HELLOL">例子 12-2</A>. </P><DIVCLASS="TIP"><P></P><TABLECLASS="TIP"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/tip.gif"HSPACE="5"ALT="Tip"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>如果脚本需要一个命令行参数, 而在调用的时候, 这个参数没被提供, 那么这就可能造成给这个参数赋一个null变量, 通常情况下, 这都会产生问题. 一种解决这个问题的办法就是使用添加额外字符的方法, 在使用这个位置参数的变量和位置参数本身的后边全部添加同样的额外字符. </P></TD></TR></TABLE></DIV><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 variable1_=$1_ # 而不是 variable1=$1 2 # 这将阻止报错, 即使在调用时没提供这个位置参数. 3 4 critical_argument01=$variable1_ 5 6 # 这个扩展的字符是可以被消除掉的, 就像这样. 7 variable1=${variable1_/_/} 8 # 副作用就是$variable1_多了一个下划线. 9 # 这里使用了参数替换模版的一种形式(后边会有具体的讨论). 10 # (在一个删除动作中, 节省了一个替换模式.) 11 12 # 处理这个问题的一个更简单的办法就是 13 #+ 判断一下这个位置参数是否传递下来了. 14 if [ -z $1 ] 15 then 16 exit $E_MISSING_POS_PARAM 17 fi 18 19 20 # 然而, 象Fabian Kreutz所指出的那样, 21 #+ 上边的方法将可能产生一个意外的副作用. 22 # 参数替换才是更好的方法: 23 # ${1:-$DefaultVal} 24 # 具体参见"参数替换"的相关章节 25 #+ 在"变量重游"那章.</PRE></FONT></TD></TR></TABLE><P>---</P><DIVCLASS="EXAMPLE"><HR><ANAME="EX18"></A><P><B>例子 4-6. <BCLASS="COMMAND">wh</B>, <AHREF="communications.html#WHOISREF">whois</A>节点名字查询</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # ex18.sh 3 4 # 是否'whois domain-name'能够找到如下3个服务之一: 5 # ripe.net, cw.net, radb.net 6 7 # 把这个脚本重命名为'wh', 然后放到/usr/local/bin目录下. 8 9 # 需要符号链接: 10 # ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe 11 # ln -s /usr/local/bin/wh /usr/local/bin/wh-cw 12 # ln -s /usr/local/bin/wh /usr/local/bin/wh-radb 13 14 E_NOARGS=65 15 16 17 if [ -z "$1" ] 18 then 19 echo "Usage: `basename $0` [domain-name]" 20 exit $E_NOARGS 21 fi 22 23 # 检查脚本名字, 然后调用合适的服务. 24 case `basename $0` in # Or: case ${0##*/} in 25 "wh" ) whois $1@whois.ripe.net;; 26 "wh-ripe") whois $1@whois.ripe.net;; 27 "wh-radb") whois $1@whois.radb.net;; 28 "wh-cw" ) whois $1@whois.cw.net;; 29 * ) echo "Usage: `basename $0` [domain-name]";; 30 esac 31 32 exit $?</PRE></FONT></TD></TR></TABLE><HR></DIV><P>---</P><P><ANAME="SHIFTREF"></A></P><P> <BCLASS="COMMAND">shift</B>命令会重新分配位置参数, 其实就是把所有的位置参数都向左移动一个位置. </P><P><CODECLASS="VARNAME">$1</CODE> <--- <CODECLASS="VARNAME">$2</CODE>, <CODECLASS="VARNAME">$2</CODE> <--- <CODECLASS="VARNAME">$3</CODE>, <CODECLASS="VARNAME">$3</CODE> <--- <CODECLASS="VARNAME">$4</CODE>, 等等.</P><P>原来的<CODECLASS="VARNAME">$1</CODE>就消失了, 但是<EM><CODECLASS="VARNAME">$0</CODE> (脚本名)是不会改变的</EM>. 如果传递了大量的位置参数到脚本中, 那么<BCLASS="COMMAND">shift</B>命令允许你访问的位置参数的数量超过<TTCLASS="LITERAL">10</TT>个, 当然<AHREF="othertypesv.html#BRACKETNOTATION">{}标记法</A>也提供了这样的功能. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX19"></A><P><B>例子 4-7. 使用<BCLASS="COMMAND">shift</B>命令</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 使用'shift'来逐步存取所有的位置参数. 3 4 # 给脚本命个名, 比如shft, 5 #+ 然后给脚本传递一些位置参数, 比如: 6 # ./shft a b c def 23 skidoo 7 8 until [ -z "$1" ] # 直到所有的位置参数都被存取完... 9 do 10 echo -n "$1 " 11 shift 12 done 13 14 echo # 额外的换行. 15 16 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>在将参数传递到<AHREF="functions.html#FUNCTIONREF">函数</A>中的时候, <BCLASS="COMMAND">shift</B>命令的工作方式也差不多. 参考<AHREF="assortedtips.html#MULTIPLICATION">例子 33-15</A>. </P></TD></TR></TABLE></DIV></DD></DL></DIV></DIV><H3CLASS="FOOTNOTES">注意事项</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN1957"HREF="othertypesv.html#AEN1957"><SPANCLASS="footnote">[1]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P><CODECLASS="VARNAME">$0</CODE>参数是由调用这个脚本的进程所设置的. 按照约定, 这个参数一般就是脚本的名字. 具体请参考<BCLASS="COMMAND">execv</B>的man页.</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="untyped.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="quoting.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Bash变量是不区分类型的</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="variables.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">引用</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?