函数可以处理传递给它们的参数,并向脚本返回一个 exit status 以供进一步处理。
function_name $arg1 $arg2 |
函数通过位置引用传递的参数(就像它们是 positional parameters 一样),即:$1, $2等等。
示例 24-2. 接受参数的函数
#!/bin/bash
# Functions and parameters
DEFAULT=default # Default param value.
func2 () {
if [ -z "$1" ] # Is parameter #1 zero length?
then
echo "-Parameter #1 is zero length.-" # Or no parameter passed.
else
echo "-Parameter #1 is \"$1\".-"
fi
variable=${1-$DEFAULT} # What does
echo "variable = $variable" #+ parameter substitution show?
# ---------------------------
# It distinguishes between
#+ no param and a null param.
if [ "$2" ]
then
echo "-Parameter #2 is \"$2\".-"
fi
return 0
}
echo
echo "Nothing passed."
func2 # Called with no params
echo
echo "Zero-length parameter passed."
func2 "" # Called with zero-length param
echo
echo "Null parameter passed."
func2 "$uninitialized_param" # Called with uninitialized param
echo
echo "One parameter passed."
func2 first # Called with one param
echo
echo "Two parameters passed."
func2 first second # Called with two params
echo
echo "\"\" \"second\" passed."
func2 "" second # Called with zero-length first parameter
echo # and ASCII string as a second one.
exit 0 |
但是,传递给脚本的命令行参数呢?函数能看到它们吗?好吧,让我们澄清一下这个困惑。
示例 24-3. 函数和传递给脚本的命令行参数
#!/bin/bash
# func-cmdlinearg.sh
# Call this script with a command-line argument,
#+ something like $0 arg1.
func ()
{
echo "$1" # Echoes first arg passed to the function.
} # Does a command-line arg qualify?
echo "First call to function: no arg passed."
echo "See if command-line arg is seen."
func
# No! Command-line arg not seen.
echo "============================================================"
echo
echo "Second call to function: command-line arg passed explicitly."
func $1
# Now it's seen!
exit 0 |
与其他某些编程语言不同,shell 脚本通常只将值参数传递给函数。变量名(实际上是 指针),如果作为参数传递给函数,将被视为字符串字面量。函数按字面意义解释它们的参数。
间接变量引用(参见 示例 37-2)提供了一种笨拙的机制,用于将变量指针传递给函数。
示例 24-4. 将间接引用传递给函数
#!/bin/bash
# ind-func.sh: Passing an indirect reference to a function.
echo_var ()
{
echo "$1"
}
message=Hello
Hello=Goodbye
echo_var "$message" # Hello
# Now, let's pass an indirect reference to the function.
echo_var "${!message}" # Goodbye
echo "-------------"
# What happens if we change the contents of "hello" variable?
Hello="Hello, again!"
echo_var "$message" # Hello
echo_var "${!message}" # Hello, again!
exit 0 |
下一个合乎逻辑的问题是,参数在传递给函数之后是否可以被解引用。
示例 24-5. 解引用传递给函数的参数
#!/bin/bash
# dereference.sh
# Dereferencing parameter passed to a function.
# Script by Bruce W. Clare.
dereference ()
{
y=\$"$1" # Name of variable (not value!).
echo $y # $Junk
x=`eval "expr \"$y\" "`
echo $1=$x
eval "$1=\"Some Different Text \"" # Assign new value.
}
Junk="Some Text"
echo $Junk "before" # Some Text before
dereference Junk
echo $Junk "after" # Some Different Text after
exit 0 |
示例 24-6. 再次,解引用传递给函数的参数
#!/bin/bash
# ref-params.sh: Dereferencing a parameter passed to a function.
# (Complex Example)
ITERATIONS=3 # How many times to get input.
icount=1
my_read () {
# Called with my_read varname,
#+ outputs the previous value between brackets as the default value,
#+ then asks for a new value.
local local_var
echo -n "Enter a value "
eval 'echo -n "[$'$1'] "' # Previous value.
# eval echo -n "[\$$1] " # Easier to understand,
#+ but loses trailing space in user prompt.
read local_var
[ -n "$local_var" ] && eval $1=\$local_var
# "And-list": if "local_var" then set "$1" to its value.
}
echo
while [ "$icount" -le "$ITERATIONS" ]
do
my_read var
echo "Entry #$icount = $var"
let "icount += 1"
echo
done
# Thanks to Stephane Chazelas for providing this instructive example.
exit 0 |
函数返回一个值,称为 exit status(退出状态)。这类似于命令返回的 exit status。退出状态可以通过 return 语句显式指定,否则它就是函数中最后一个命令的退出状态(成功时为 0,失败时为非零错误代码)。这个 exit status 可以在脚本中通过引用 $? 来使用。这种机制有效地允许脚本函数拥有类似于 C 函数的 “返回值”。
终止一个函数。return 命令可以选择接受一个 integer(整数)参数 [1],该参数将作为函数的 “退出状态” 返回给调用脚本,并且此退出状态被赋值给变量 $?。
示例 24-7. 两个数字的最大值
#!/bin/bash
# max.sh: Maximum of two integers.
E_PARAM_ERR=250 # If less than 2 params passed to function.
EQUAL=251 # Return value if both params equal.
# Error values out of range of any
#+ params that might be fed to the function.
max2 () # Returns larger of two numbers.
{ # Note: numbers compared must be less than 250.
if [ -z "$2" ]
then
return $E_PARAM_ERR
fi
if [ "$1" -eq "$2" ]
then
return $EQUAL
else
if [ "$1" -gt "$2" ]
then
return $1
else
return $2
fi
fi
}
max2 33 34
return_val=$?
if [ "$return_val" -eq $E_PARAM_ERR ]
then
echo "Need to pass two parameters to the function."
elif [ "$return_val" -eq $EQUAL ]
then
echo "The two numbers are equal."
else
echo "The larger of the two numbers is $return_val."
fi
exit 0
# Exercise (easy):
# ---------------
# Convert this to an interactive script,
#+ that is, have the script ask for input (two numbers). |
![]() | 要使函数返回字符串或数组,请使用专用变量。
|
示例 24-8. 将数字转换为罗马数字
#!/bin/bash
# Arabic number to Roman numeral conversion
# Range: 0 - 200
# It's crude, but it works.
# Extending the range and otherwise improving the script is left as an exercise.
# Usage: roman number-to-convert
LIMIT=200
E_ARG_ERR=65
E_OUT_OF_RANGE=66
if [ -z "$1" ]
then
echo "Usage: `basename $0` number-to-convert"
exit $E_ARG_ERR
fi
num=$1
if [ "$num" -gt $LIMIT ]
then
echo "Out of range!"
exit $E_OUT_OF_RANGE
fi
to_roman () # Must declare function before first call to it.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
echo -n $rchar
let "number -= factor"
let "remainder = number - factor"
done
return $number
# Exercises:
# ---------
# 1) Explain how this function works.
# Hint: division by successive subtraction.
# 2) Extend to range of the function.
# Hint: use "echo" and command-substitution capture.
}
to_roman $num 100 C
num=$?
to_roman $num 90 LXXXX
num=$?
to_roman $num 50 L
num=$?
to_roman $num 40 XL
num=$?
to_roman $num 10 X
num=$?
to_roman $num 9 IX
num=$?
to_roman $num 5 V
num=$?
to_roman $num 4 IV
num=$?
to_roman $num 1 I
# Successive calls to conversion function!
# Is this really necessary??? Can it be simplified?
echo
exit |
另请参见 示例 11-29。
![]() | 函数可以返回的最大正整数是 255。return 命令与 exit status 的概念紧密相关,这解释了这种特定的限制。幸运的是,对于那些需要从函数返回较大整数值的情况,有各种 解决方法。 示例 24-9. 测试函数中的大返回值
获取较大整数 “返回值” 的一种解决方法是简单地将 “返回值” 赋值给全局变量。
一种更优雅的方法是让函数 echo 其 “返回值到 stdout”,然后通过 命令替换 捕获它。请参阅 第 36.7 节中对此的 讨论。 示例 24-10. 比较两个大整数
这是另一个捕获函数 “返回值” 的示例。理解它需要一些 awk 知识。
练习使用我们刚刚学到的知识,扩展之前的 罗马数字示例 以接受任意大的输入。 |
函数本质上是一个 代码块,这意味着它的stdin可以被重定向(如 示例 3-1 中所示)。
示例 24-11. 从用户名获取真实姓名
#!/bin/bash
# realname.sh
#
# From username, gets "real name" from /etc/passwd.
ARGCOUNT=1 # Expect one arg.
E_WRONGARGS=85
file=/etc/passwd
pattern=$1
if [ $# -ne "$ARGCOUNT" ]
then
echo "Usage: `basename $0` USERNAME"
exit $E_WRONGARGS
fi
file_excerpt () # Scan file for pattern,
{ #+ then print relevant portion of line.
while read line # "while" does not necessarily need [ condition ]
do
echo "$line" | grep $1 | awk -F":" '{ print $5 }'
# Have awk use ":" delimiter.
done
} <$file # Redirect into function's stdin.
file_excerpt $pattern
# Yes, this entire script could be reduced to
# grep PATTERN /etc/passwd | awk -F":" '{ print $5 }'
# or
# awk -F: '/PATTERN/ {print $5}'
# or
# awk -F: '($1 == "username") { print $5 }' # real name from username
# However, it might not be as instructive.
exit 0 |
还有一种替代方法,可能不那么令人困惑,可以重定向函数的stdin。这涉及到将stdin重定向到函数内嵌的带括号的代码块。
# Instead of:
Function ()
{
...
} < file
# Try this:
Function ()
{
{
...
} < file
}
# Similarly,
Function () # This works.
{
{
echo $*
} | tr a b
}
Function () # This doesn't work.
{
echo $*
} | tr a b # A nested code block is mandatory here.
# Thanks, S.C. |
![]() | Emmanuel Rouat 的 示例 bashrc 文件 包含一些具有启发性的函数示例。 |
| [1] | return 命令是 Bash builtin(内建命令)。 |