📄 complexfunct.html
字号:
CLASS="EXAMPLE"><HR><ANAME="EX61"></A><P><B>例子 23-8. 将阿拉伯数字转化为罗马数字</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # 将阿拉伯数字转化为罗马数字 4 # 范围: 0 - 200 5 # 比较粗糙, 但可以正常工作. 6 7 # 扩展范围, 并且完善这个脚本, 作为练习. 8 9 # 用法: roman number-to-convert 10 11 LIMIT=200 12 E_ARG_ERR=65 13 E_OUT_OF_RANGE=66 14 15 if [ -z "$1" ] 16 then 17 echo "Usage: `basename $0` number-to-convert" 18 exit $E_ARG_ERR 19 fi 20 21 num=$1 22 if [ "$num" -gt $LIMIT ] 23 then 24 echo "Out of range!" 25 exit $E_OUT_OF_RANGE 26 fi 27 28 to_roman () # 在第一次调用函数前必须先定义它. 29 { 30 number=$1 31 factor=$2 32 rchar=$3 33 let "remainder = number - factor" 34 while [ "$remainder" -ge 0 ] 35 do 36 echo -n $rchar 37 let "number -= factor" 38 let "remainder = number - factor" 39 done 40 41 return $number 42 # 练习: 43 # ----- 44 # 解释这个函数是如何工作的. 45 # 提示: 依靠不断的除, 来分割数字. 46 } 47 48 49 to_roman $num 100 C 50 num=$? 51 to_roman $num 90 LXXXX 52 num=$? 53 to_roman $num 50 L 54 num=$? 55 to_roman $num 40 XL 56 num=$? 57 to_roman $num 10 X 58 num=$? 59 to_roman $num 9 IX 60 num=$? 61 to_roman $num 5 V 62 num=$? 63 to_roman $num 4 IV 64 num=$? 65 to_roman $num 1 I 66 67 echo 68 69 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>也请参考<AHREF="testbranch.html#ISALPHA">例子 10-28</A>. </P><DIVCLASS="IMPORTANT"><P></P><TABLECLASS="IMPORTANT"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/important.gif"HSPACE="5"ALT="Important"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>函数所能返回最大的正整数是255. <BCLASS="COMMAND">return</B>命令与<AHREF="exit-status.html#EXITSTATUSREF">退出状态码</A>的概念被紧密联系在一起, 并且退出状态码的值受此限制. 幸运的是, 如果想让函数返回大整数的话, 有好多种不同的<AHREF="assortedtips.html#RVT">工作区</A>能够应付这个情况. </P><DIVCLASS="EXAMPLE"><HR><ANAME="RETURNTEST"></A><P><B>例子 23-9. 测试函数最大的返回值</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # return-test.sh 3 4 # 函数所能返回的最大正整数为255. 5 6 return_test () # 传给函数什么值, 就返回什么值. 7 { 8 return $1 9 } 10 11 return_test 27 # o.k. 12 echo $? # 返回27. 13 14 return_test 255 # 依然是o.k. 15 echo $? # 返回255. 16 17 return_test 257 # 错误! 18 echo $? # 返回1 (对应各种错误的返回码). 19 20 # ====================================================== 21 return_test -151896 # 能返回一个大负数么? 22 echo $? # 能否返回-151896? 23 # 显然不行! 只返回了168. 24 # Bash 2.05b以前的版本 25 #+ 允许返回大负数. 26 # Bash的新版本(2.05b之后)修正了这个漏洞. 27 # 这可能会影响以前所编写的脚本. 28 # 一定要小心! 29 # ====================================================== 30 31 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>如果你想获得大整数<SPANCLASS="QUOTE">"返回值"</SPAN>的话, 其实最简单的办法就是将<SPANCLASS="QUOTE">"要返回的值"</SPAN>保存到一个全局变量中. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 Return_Val= # 用于保存函数特大返回值的全局变量. 2 3 alt_return_test () 4 { 5 fvar=$1 6 Return_Val=$fvar 7 return # 返回0 (成功). 8 } 9 10 alt_return_test 1 11 echo $? # 0 12 echo "return value = $Return_Val" # 1 13 14 alt_return_test 256 15 echo "return value = $Return_Val" # 256 16 17 alt_return_test 257 18 echo "return value = $Return_Val" # 257 19 20 alt_return_test 25701 21 echo "return value = $Return_Val" #25701</PRE></FONT></TD></TR></TABLE> </P><P>一种更优雅的做法是在函数中使用<BCLASS="COMMAND">echo</B>命令将<SPANCLASS="QUOTE">"返回值输出到<TTCLASS="FILENAME">stdout</TT>"</SPAN>, 然后使用<AHREF="commandsub.html#COMMANDSUBREF">命令替换</A>来捕捉此值. 请参考<AHREF="assortedtips.html">Section 33.7</A>中<AHREF="assortedtips.html#RVT">关于这种用法的讨论</A>. </P><DIVCLASS="EXAMPLE"><HR><ANAME="MAX2"></A><P><B>例子 23-10. 比较两个大整数</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # max2.sh: 取两个大整数中的最大值. 3 4 # 这是前一个例子"max.sh"的修改版, 5 #+ 这个版本可以比较两个大整数. 6 7 EQUAL=0 # 如果两个值相等, 那就返回这个值. 8 E_PARAM_ERR=-99999 # 没有足够多的参数传递给函数. 9 # ^^^^^^ 任意超出范围的参数都可以传递进来. 10 11 max2 () # "返回"两个整数中最大的那个. 12 { 13 if [ -z "$2" ] 14 then 15 echo $E_PARAM_ERR 16 return 17 fi 18 19 if [ "$1" -eq "$2" ] 20 then 21 echo $EQUAL 22 return 23 else 24 if [ "$1" -gt "$2" ] 25 then 26 retval=$1 27 else 28 retval=$2 29 fi 30 fi 31 32 echo $retval # 输出(到stdout), 而没有用返回值. 33 # 为什么? 34 } 35 36 37 return_val=$(max2 33001 33997) 38 # ^^^^ 函数名 39 # ^^^^^ ^^^^^ 传递进来的参数 40 # 这其实是命令替换的一种形式: 41 #+ 可以把函数看作为一个命令, 42 #+ 然后把函数的stdout赋值给变量"return_val." 43 44 45 # ========================= OUTPUT ======================== 46 if [ "$return_val" -eq "$E_PARAM_ERR" ] 47 then 48 echo "Error in parameters passed to comparison function!" 49 elif [ "$return_val" -eq "$EQUAL" ] 50 then 51 echo "The two numbers are equal." 52 else 53 echo "The larger of the two numbers is $return_val." 54 fi 55 # ========================================================= 56 57 exit 0 58 59 # 练习: 60 # ----- 61 # 1) 找到一种更优雅的方法, 62 #+ 来测试传递给函数的参数. 63 # 2) 简化"输出"段的if/then结构. 64 # 3) 重写这个脚本, 使其能够从命令行参数中获得输入. </PRE></FONT></TD></TR></TABLE><HR></DIV><P>这是另一个能够捕捉函数<SPANCLASS="QUOTE">"返回值"</SPAN>的例子. 要想搞明白这个例子, 需要一些<AHREF="awk.html#AWKREF">awk</A>的知识. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 month_length () # 把月份作为参数. 2 { # 返回该月包含的天数. 3 monthD="31 28 31 30 31 30 31 31 30 31 30 31" # 作为局部变量声明? 4 echo "$monthD" | awk '{ print $'"${1}"' }' # 小技巧. 5 # ^^^^^^^^^ 6 # 传递给函数的参数($1 -- 月份号), 然后传给awk. 7 # Awk把参数解释为"print $1 . . . print $12"(这依赖于月份号) 8 # 这是一个模版, 用于将参数传递给内嵌awk的脚本: 9 # $'"${script_parameter}"' 10 11 # 需要做一些错误检查, 来保证月份号正确, 在范围(1-12)之间, 12 #+ 别忘了检查闰年的二月. 13 } 14 15 # ---------------------------------------------- 16 # 用例: 17 month=4 # 以四月为例. 18 days_in=$(month_length $month) 19 echo $days_in # 30 20 # ----------------------------------------------</PRE></FONT></TD></TR></TABLE></P><P>也请参考<AHREF="contributed-scripts.html#DAYSBETWEEN">例子 A-7</A>. </P><P><KBDCLASS="USERINPUT">练习:</KBD> 使用目前我们已经学到的知识, 来扩展之前的例子<AHREF="complexfunct.html#EX61">将阿拉伯数字转化为罗马数字</A>, 让它能够接受任意大的输入. </P></TD></TR></TABLE></DIV></DD></DL></DIV><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="REDSTDINFUNC1"></A>重定向</B></P><DL><DT><TTCLASS="REPLACEABLE"><I>重定向函数的stdin</I></TT></DT><DD><P>函数本质上其实就是一个<AHREF="special-chars.html#CODEBLOCKREF">代码块</A>, 这就意味着它的<TTCLASS="FILENAME">stdin</TT>可以被重定向(比如<AHREF="special-chars.html#EX8">例子 3-1</A>). </P><DIVCLASS="EXAMPLE"><HR><ANAME="REALNAME"></A><P><B>例子 23-11. 从username中取得用户的真名</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # realname.sh 3 # 4 # 依靠username, 从/etc/passwd中获得"真名". 5 6 7 ARGCOUNT=1 # 需要一个参数. 8 E_WRONGARGS=65 9 10 file=/etc/passwd 11 pattern=$1 12 13 if [ $# -ne "$ARGCOUNT" ] 14 then 15 echo "Usage: `basename $0` USERNAME" 16 exit $E_WRONGARGS 17 fi 18 19 file_excerpt () # 按照要求的模式来扫描文件, 然后打印文件相关的部分. 20 { 21 while read line # "while"并不一定非得有"[ condition ]"不可. 22 do 23 echo "$line" | grep $1 | awk -F":" '{ print $5 }' # awk用":"作为界定符. 24 done 25 } <$file # 重定向到函数的stdin. 26 27 file_excerpt $pattern 28 29 # 是的, 整个脚本其实可以被缩减为 30 # grep PATTERN /etc/passwd | awk -F":" '{ print $5 }' 31 # 或 32 # awk -F: '/PATTERN/ {print $5}' 33 # 或 34 # awk -F: '($1 == "username") { print $5 }' # 从username中获得真名. 35 # 但是, 这些起不到示例的作用. 36 37 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>还有一个办法, 或许能够更好的理解重定向函数的<TTCLASS="FILENAME">stdin</TT>. 它在函数内添加了一对大括号, 并且将重定向<TTCLASS="FILENAME">stdin</TT>的行为放在这对添加的大括号上. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 # 用下面的方法来代替它: 2 Function () 3 { 4 ... 5 } < file 6 7 # 试试这个: 8 Function () 9 { 10 { 11 ... 12 } < file 13 } 14 15 # 同样的, 16 17 Function () # 没问题. 18 { 19 { 20 echo $* 21 } | tr a b 22 } 23 24 Function () # 不行. 25 { 26 echo $* 27 } | tr a b # 这儿的内嵌代码块是被强制的. 28 29 30 # 感谢, S.C.</PRE></FONT></TD></TR></TABLE> </P></DD></DL></DIV></DIV><H3CLASS="FOOTNOTES">注意事项</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN14444"HREF="complexfunct.html#AEN14444"><SPANCLASS="footnote">[1]</SPAN></A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P><BCLASS="COMMAND">return</B>命令是Bash<AHREF="internal.html#BUILTINREF">内建命令builtin</A>. </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="functions.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="localvar.html"ACCESSKEY="N">下一页</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">函数</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="functions.html"ACCESSKEY="U">上一级</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">局部变量</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -