📄 complexfunct.html
字号:
<!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="functions.html"><LINKREL="PREVIOUS"TITLE="函数"HREF="functions.html"><LINKREL="NEXT"TITLE="局部变量"HREF="localvar.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="functions.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom">23. 函数</TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="localvar.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="SECT1"><H1CLASS="SECT1"><ANAME="COMPLEXFUNCT">23.1. 复杂函数和函数复杂性</A></H1><P>函数可以处理传递给它的参数, 并且能返回它的<AHREF="exit-status.html#EXITSTATUSREF">退出状态码</A>给脚本, 以便后续处理. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 function_name $arg1 $arg2</PRE></FONT></TD></TR></TABLE><P>函数以位置来引用传递过来的参数(就好像它们是<AHREF="internalvariables.html#POSPARAMREF">位置参数</A>), 例如, <CODECLASS="VARNAME">$1</CODE>, <CODECLASS="VARNAME">$2</CODE>, 等等. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX60"></A><P><B>例子 23-2. 带参数的函数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 函数和参数 3 4 DEFAULT=default # 默认参数值. 5 6 func2 () { 7 if [ -z "$1" ] # 第一个参数是否长度为零? 8 then 9 echo "-Parameter #1 is zero length.-" # 或者没有参数被传递进来. 10 else 11 echo "-Param #1 is \"$1\".-" 12 fi 13 14 variable=${1-$DEFAULT} # 这里的参数替换 15 echo "variable = $variable" #+ 表示什么? 16 # --------------------------- 17 # 为了区分没有参数的情况, 18 #+ 和只有一个null参数的情况. 19 20 if [ "$2" ] 21 then 22 echo "-Parameter #2 is \"$2\".-" 23 fi 24 25 return 0 26 } 27 28 echo 29 30 echo "Nothing passed." 31 func2 # 不带参数调用 32 echo 33 34 35 echo "Zero-length parameter passed." 36 func2 "" # 使用0长度的参数进行调用 37 echo 38 39 echo "Null parameter passed." 40 func2 "$uninitialized_param" # 使用未初始化的参数进行调用 41 echo 42 43 echo "One parameter passed." 44 func2 first # 带一个参数调用 45 echo 46 47 echo "Two parameters passed." 48 func2 first second # 带两个参数调用 49 echo 50 51 echo "\"\" \"second\" passed." 52 func2 "" second # 带两个参数调用, 53 echo # 第一个参数长度为0, 第二个参数是由ASCII码组成的字符串. 54 55 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="IMPORTANT"><P></P><TABLECLASS="IMPORTANT"WIDTH="100%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/important.gif"HSPACE="5"ALT="Important"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>也可以使用<AHREF="othertypesv.html#SHIFTREF">shift</A>命令来处理传递给函数的参数(请参考<AHREF="assortedtips.html#MULTIPLICATION">例子 33-15</A>). </P></TD></TR></TABLE></DIV><P>但是, 传给脚本的命令行参数怎么办? 在函数内部, 这些传给脚本的命令行参数也可见么? 好, 现在让我们弄清楚这个问题. </P><DIVCLASS="EXAMPLE"><HR><ANAME="FUNCCMDLINEARG"></A><P><B>例子 23-3. 函数与传递给脚本的命令行参数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # func-cmdlinearg.sh 3 # 调用这个脚本, 并且带一个命令行参数. 4 #+ 类似于 $0 arg1. 5 6 7 func () 8 9 { 10 echo "$1" 11 } 12 13 echo "First call to function: no arg passed." 14 echo "See if command-line arg is seen." 15 func 16 # 不行! 命令行参数不可见. 17 18 echo "============================================================" 19 echo 20 echo "Second call to function: command-line arg passed explicitly." 21 func $1 22 # 现在可见了! 23 24 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>与别的编程语言相比, shell脚本一般只会传值给函数. 如果把变量名(事实上就是指针)作为参数传递给函数的话, 那将被解释为字面含义, 也就是被看作字符串. <EM>函数只会以字面含义来解释函数参数. </EM></P><P><AHREF="ivr.html#IVRREF">变量的间接引用</A>(请参考<AHREF="bashver2.html#EX78">例子 34-2</A>)提供了一种笨拙的机制, 来将变量指针传递给函数. </P><DIVCLASS="EXAMPLE"><HR><ANAME="INDFUNC"></A><P><B>例子 23-4. 将一个间接引用传递给函数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # ind-func.sh: 将一个间接引用传递给函数. 3 4 echo_var () 5 { 6 echo "$1" 7 } 8 9 message=Hello 10 Hello=Goodbye 11 12 echo_var "$message" # Hello 13 # 现在,让我们传递一个间接引用给函数. 14 echo_var "${!message}" # Goodbye 15 16 echo "-------------" 17 18 # 如果我们改变"hello"变量的值会发生什么? 19 Hello="Hello, again!" 20 echo_var "$message" # Hello 21 echo_var "${!message}" # Hello, again! 22 23 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>接下来的一个逻辑问题就是, 将参数传递给函数<EM>之后</EM>, 参数能否被解除引用. </P><DIVCLASS="EXAMPLE"><HR><ANAME="DEREFERENCECL"></A><P><B>例子 23-5. 对一个传递给函数的参数进行解除引用的操作</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # dereference.sh 3 # 对一个传递给函数的参数进行解除引用的操作. 4 # 此脚本由Bruce W. Clare所编写. 5 6 dereference () 7 { 8 y=\$"$1" # 变量名. 9 echo $y # $Junk 10 11 x=`eval "expr \"$y\" "` 12 echo $1=$x 13 eval "$1=\"Some Different Text \"" # 赋新值. 14 } 15 16 Junk="Some Text" 17 echo $Junk "before" # Some Text before 18 19 dereference Junk 20 echo $Junk "after" # Some Different Text after 21 22 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="EXAMPLE"><HR><ANAME="REFPARAMS"></A><P><B>例子 23-6. 再来一次, 对一个传递给函数的参数进行解除引用的操作</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # ref-params.sh: 解除传递给函数的参数引用. 3 # (复杂的例子) 4 5 ITERATIONS=3 # 取得输入的次数. 6 icount=1 7 8 my_read () { 9 # 用my_read varname这种形式来调用, 10 #+ 将之前用括号括起的值作为默认值输出, 11 #+ 然后要求输入一个新值. 12 13 local local_var 14 15 echo -n "Enter a value " 16 eval 'echo -n "[$'$1'] "' # 之前的值. 17 # eval echo -n "[\$$1] " # 更容易理解, 18 #+ 但会丢失用户在尾部输入的空格. 19 read local_var 20 [ -n "$local_var" ] && eval $1=\$local_var 21 22 # "与列表": 如果"local_var"的测试结果为true, 则把变量"$1"的值赋给它. 23 } 24 25 echo 26 27 while [ "$icount" -le "$ITERATIONS" ] 28 do 29 my_read var 30 echo "Entry #$icount = $var" 31 let "icount += 1" 32 echo 33 done 34 35 36 # 感谢Stephane Chazelas提供这个例子. 37 38 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="EXITRETURN1"></A>退出与返回</B></P><DL><DT><BCLASS="COMMAND">退出状态码</B></DT><DD><P>函数返回一个值, 被称为<ICLASS="FIRSTTERM">退出状态码</I>. 退出状态码可以由<BCLASS="COMMAND">return</B>命令明确指定, 也可以由函数中最后一条命令的退出状态码来指定(如果成功则返回<SPANCLASS="RETURNVALUE">0</SPAN>, 否则返回非0值). 可以在脚本中使用<AHREF="internalvariables.html#XSTATVARREF">$?</A>来引用<AHREF="exit-status.html#EXITSTATUSREF">退出状态码</A>. 因为有了这种机制, 所以脚本函数也可以象C函数一样有<SPANCLASS="QUOTE">"返回值"</SPAN>. </P></DD><DT><BCLASS="COMMAND">return</B></DT><DD><P><ANAME="RETURNREF"></A></P><P>终止一个函数. <BCLASS="COMMAND">return</B>命令 <ANAME="AEN14444"HREF="#FTN.AEN14444"><SPANCLASS="footnote">[1]</SPAN></A> 可选的允许带一个<EM>整型</EM>参数, 这个整数将作为函数的<SPANCLASS="QUOTE">"退出状态码"</SPAN>返回给调用这个函数的脚本, 并且这个整数也被赋值给变量<AHREF="internalvariables.html#XSTATVARREF">$?</A>. </P><DIVCLASS="EXAMPLE"><HR><ANAME="MAX"></A><P><B>例子 23-7. 取两个数中的最大值</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # max.sh: 取两个整数中的最大值. 3 4 E_PARAM_ERR=-198 # 如果传给函数的参数少于2个时, 就返回这个值. 5 EQUAL=-199 # 如果两个整数相等时, 返回这个值. 6 # 任意超出范围的 7 #+ 参数值都可能传递到函数中. 8 9 max2 () # 返回两个整数中的最大值. 10 { # 注意: 参与比较的数必须小于257. 11 if [ -z "$2" ] 12 then 13 return $E_PARAM_ERR 14 fi 15 16 if [ "$1" -eq "$2" ] 17 then 18 return $EQUAL 19 else 20 if [ "$1" -gt "$2" ] 21 then 22 return $1 23 else 24 return $2 25 fi 26 fi 27 } 28 29 max2 33 34 30 return_val=$? 31 32 if [ "$return_val" -eq $E_PARAM_ERR ] 33 then 34 echo "Need to pass two parameters to the function." 35 elif [ "$return_val" -eq $EQUAL ] 36 then 37 echo "The two numbers are equal." 38 else 39 echo "The larger of the two numbers is $return_val." 40 fi 41 42 43 exit 0 44 45 # 练习(简单): 46 # ----------- 47 # 把这个脚本转化为交互式脚本, 48 #+ 也就是, 修改这个脚本, 让其要求调用者输入2个数. </PRE></FONT></TD></TR></TABLE><HR></DIV><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>为了让函数可以返回字符串或是数组, 可以使用一个在函数外可见的专用全局变量. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 count_lines_in_etc_passwd() 2 { 3 [[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd)) 4 # 如果/etc/passwd是可读的, 那么就把REPLY设置为文件的行数. 5 # 这样就可以同时返回参数值与状态信息. 6 # 'echo'看上去没什么用, 可是 . . . 7 #+ 它的作用是删除输出中的多余空白字符. 8 } 9 10 if count_lines_in_etc_passwd 11 then 12 echo "There are $REPLY lines in /etc/passwd." 13 else 14 echo "Cannot count lines in /etc/passwd." 15 fi 16 17 # 感谢, S.C.</PRE></FONT></TD></TR></TABLE> </P></TD></TR></TABLE></DIV><DIV
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -