📄 functions.html
字号:
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></TD></TR></TABLE><HR></DIV><P>See also <AHREF="testbranch.html#ISALPHA">Example 10-28</A>.</P><DIVCLASS="IMPORTANT"><TABLECLASS="IMPORTANT"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="common/important.png"HSPACE="5"ALT="Important"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>The largest positive integer a function can return is 255. The <BCLASS="COMMAND">return</B> command is closely tied to the concept of <AHREF="exit-status.html#EXITSTATUSREF">exit status</A>, which accounts for this particular limitation. Fortunately, there are various <AHREF="assortedtips.html#RVT">workarounds</A> for those situations requiring a large integer return value from a function.</P><DIVCLASS="EXAMPLE"><HR><ANAME="RETURNTEST"></A><P><B>Example 23-9. Testing large return values in a function</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # return-test.sh 3 4 # The largest positive value a function can return is 255. 5 6 return_test () # Returns whatever passed to it. 7 { 8 return $1 9 } 10 11 return_test 27 # o.k. 12 echo $? # Returns 27. 13 14 return_test 255 # Still o.k. 15 echo $? # Returns 255. 16 17 return_test 257 # Error! 18 echo $? # Returns 1 (return code for miscellaneous error). 19 20 # ====================================================== 21 return_test -151896 # Do large negative numbers work? 22 echo $? # Will this return -151896? 23 # No! It returns 168. 24 # Version of Bash before 2.05b permitted 25 #+ large negative integer return values. 26 # Newer versions of Bash plug this loophole. 27 # This may break older scripts. 28 # Caution! 29 # ====================================================== 30 31 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>A workaround for obtaining large integer <SPANCLASS="QUOTE">"return values"</SPAN> is to simply assign the <SPANCLASS="QUOTE">"return value"</SPAN> to a global variable. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 Return_Val= # Global variable to hold oversize return value of function. 2 3 alt_return_test () 4 { 5 fvar=$1 6 Return_Val=$fvar 7 return # Returns 0 (success). 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></TD></TR></TABLE> </P><P><ANAME="CAPTURERETVAL"></A></P><P>A more elegant method is to have the function <BCLASS="COMMAND">echo</B> its <SPANCLASS="QUOTE">"return value to <TTCLASS="FILENAME">stdout</TT>,"</SPAN> and then capture it by <AHREF="commandsub.html#COMMANDSUBREF">command substitution</A>. See the <AHREF="assortedtips.html#RVT">discussion of this</A> in <AHREF="assortedtips.html">Section 33.8</A>.</P><DIVCLASS="EXAMPLE"><HR><ANAME="MAX2"></A><P><B>Example 23-10. Comparing two large integers</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # max2.sh: Maximum of two LARGE integers. 3 4 # This is the previous "max.sh" example, 5 #+ modified to permit comparing large integers. 6 7 EQUAL=0 # Return value if both params equal. 8 E_PARAM_ERR=-99999 # Not enough params passed to function. 9 # ^^^^^^ Out of range of any params that might be passed. 10 11 max2 () # "Returns" larger of two numbers. 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 # Echoes (to stdout), rather than returning value. 33 # Why? 34 } 35 36 37 return_val=$(max2 33001 33997) 38 # ^^^^ Function name 39 # ^^^^^ ^^^^^ Params passed 40 # This is actually a form of command substitution: 41 #+ treating a function as if it were a command, 42 #+ and assigning the stdout of the function to the variable "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 # Exercises: 60 # --------- 61 # 1) Find a more elegant way of testing 62 #+ the parameters passed to the function. 63 # 2) Simplify the if/then structure at "OUTPUT." 64 # 3) Rewrite the script to take input from command-line parameters.</PRE></TD></TR></TABLE><HR></DIV><P>Here is another example of capturing a function <SPANCLASS="QUOTE">"return value."</SPAN> Understanding it requires some knowledge of <AHREF="awk.html#AWKREF">awk</A>. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 month_length () # Takes month number as an argument. 2 { # Returns number of days in month. 3 monthD="31 28 31 30 31 30 31 31 30 31 30 31" # Declare as local? 4 echo "$monthD" | awk '{ print $'"${1}"' }' # Tricky. 5 # ^^^^^^^^^ 6 # Parameter passed to function ($1 -- month number), then to awk. 7 # Awk sees this as "print $1 . . . print $12" (depending on month number) 8 # Template for passing a parameter to embedded awk script: 9 # $'"${script_parameter}"' 10 11 # Needs error checking for correct parameter range (1-12) 12 #+ and for February in leap year. 13 } 14 15 # ---------------------------------------------- 16 # Usage example: 17 month=4 # April, for example (4th month). 18 days_in=$(month_length $month) 19 echo $days_in # 30 20 # ----------------------------------------------</PRE></TD></TR></TABLE></P><P>See also <AHREF="contributed-scripts.html#DAYSBETWEEN">Example A-7</A>.</P><P><TTCLASS="USERINPUT"><B>Exercise:</B></TT> Using what we have just learned, extend the previous <AHREF="functions.html#EX61">Roman numerals example</A> to accept arbitrarily large input.</P></TD></TR></TABLE></DIV></DD></DL></DIV><DIVCLASS="VARIABLELIST"><P><B><ANAME="REDSTDINFUNC1"></A>Redirection</B></P><DL><DT><TTCLASS="REPLACEABLE"><I>Redirecting the stdin of a function</I></TT></DT><DD><P>A function is essentially a <AHREF="special-chars.html#CODEBLOCKREF">code block</A>, which means its <TTCLASS="FILENAME">stdin</TT> can be redirected (as in <AHREF="special-chars.html#EX8">Example 3-1</A>).</P><DIVCLASS="EXAMPLE"><HR><ANAME="REALNAME"></A><P><B>Example 23-11. Real name from username</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # realname.sh 3 # 4 # From username, gets "real name" from /etc/passwd. 5 6 7 ARGCOUNT=1 # Expect one arg. 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 () # Scan file for pattern, then print relevant portion of line. 20 { 21 while read line # "while" does not necessarily need "[ condition ]" 22 do 23 echo "$line" | grep $1 | awk -F":" '{ print $5 }' # Have awk use ":" delimiter. 24 done 25 } <$file # Redirect into function's stdin. 26 27 file_excerpt $pattern 28 29 # Yes, this entire script could be reduced to 30 # grep PATTERN /etc/passwd | awk -F":" '{ print $5 }' 31 # or 32 # awk -F: '/PATTERN/ {print $5}' 33 # or 34 # awk -F: '($1 == "username") { print $5 }' # real name from username 35 # However, it might not be as instructive. 36 37 exit 0</PRE></TD></TR></TABLE><HR></DIV><P>There is an alternate, and perhaps less confusing method of redirecting a function's <TTCLASS="FILENAME">stdin</TT>. This involves redirecting the <TTCLASS="FILENAME">stdin</TT> to an embedded bracketed code block within the function. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><PRECLASS="PROGRAMLISTING"> 1 # Instead of: 2 Function () 3 { 4 ... 5 } < file 6 7 # Try this: 8 Function () 9 { 10 { 11 ... 12 } < file 13 } 14 15 # Similarly, 16 17 Function () # This works. 18 { 19 { 20 echo $* 21 } | tr a b 22 } 23 24 Function () # This doesn't work. 25 { 26 echo $* 27 } | tr a b # A nested code block is mandatory here. 28 29 30 # Thanks, S.C.</PRE></TD></TR></TABLE> </P></DD></DL></DIV></DIV></DIV><H3CLASS="FOOTNOTES">Notes</H3><TABLEBORDER="0"CLASS="FOOTNOTES"WIDTH="100%"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="5%"><ANAME="FTN.AEN17024"HREF="functions.html#AEN17024">[1]</A></TD><TDALIGN="LEFT"VALIGN="TOP"WIDTH="95%"><P>The <BCLASS="COMMAND">return</B> command is a 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="process-sub.html"ACCESSKEY="P">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html"ACCESSKEY="H">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="localvar.html"ACCESSKEY="N">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Process Substitution</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="part5.html"ACCESSKEY="U">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">Local Variables</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -