# Linux Shell自定义函数
在脚本中一遍又一遍地编写同样的代码会很烦人,为了避免这种麻烦,可以将代码封装成函数,多次引用。
# 1)函数的定义与使用
Linux Shell
中函数的定义有两种方式:
- 采用关键字
function
function name {
commands
}
- 函数名后跟空括号
name() {
commands
}
使用函数:
#!/bin/bash
sayHello
function sayHello {
echo "Hello, Bash Shell."
}
sayHi() {
echo "Hi, Bash Shell."
}
sayHello
sayHi
sayHi() {
echo "Hi2, Bash Shell."
}
sayHi
执行,
$ ./test.sh
# ./test.sh: 行 3: sayHello:未找到命令
# Hello, Bash Shell.
# Hi, Bash Shell.
# Hi2, Bash Shell.
每次引用函数名时,bash shell
会找到函数的定义并执行在那里定义的命令。
从上面的例子可以看到,
- 使用未定义的函数脚本会报错
- 函数可以被重定义,调用时会使用最新的定义,原来的定义会被覆盖
# 2)函数的返回值
通常,在使用函数时为了与调用者交互,会希望函数能给出局部逻辑执行后的返回值,如运算结果,执行有没有出错。
# 默认情况
在bash shell
中,默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。函数执行结束后,可以用标准变量 $?
来确定函数的退出状态码。
#!/bin/bash
function sayHello {
echo "Hello, Bash Shell."
}
sayHello
echo ret value: $?
sayHi() {
echo "Hi2, Bash Shell."
ls -al rob
}
sayHi
echo ret value: $?
sayHi() {
ls -al rob
echo "Hi2, Bash Shell."
}
执行,
$ ./test.sh
# Hello, Bash Shell.
# ret value: 0
# Hi2, Bash Shell.
# ls: 无法访问 'rob': 没有那个文件或目录
# ret value: 2
# ls: 无法访问 'rob': 没有那个文件或目录
# Hi2, Bash Shell.
# ret value: 0
可以看到,在sayHi
函数中执行最后一行指令ls -al rob
时出错,因此$?
的值是2
。但是,看上面的例子中,前后两个sayHi
函数因为出错语句位置的不同,函数的退出状态也不同。因此,使用$?
判断函数的状态是很不可靠的。
# 使用return
命令
bash shell
使用 return
命令来退出函数并返回特定的退出状态码。 return
命令允许指定一个整数值来定义函数的退出状态码。
return
返回的状态码也在$?
中,必须函数一结束就返回,否则返回值会结束- 退出状态码必须是
0-255
,任何大于256的值都会产生一个错误值。
#!/bin/bash
doubleCount(){
read -p "Please Input A Value:" ival
echo doubling the value...
return $[ $ival * 2 ]
}
doubleCount
echo "The New Value:" $?
执行,
$ ./test.sh
# Please Input A Value:100
# doubling the value...
# The New Value: 200
$ ./test.sh
# Please Input A Value:200
# doubling the value...
# The New Value: 144
可以看到输入200
时,输出本应该是400
,但因为其超出了256
的范围,因此输出错误。
# 捕获函数输出
可以将函数的输出保存到shell
变量中。
#!/bin/bash
doubleCount(){
read -p "Please Input A Value:" ival
echo $[ $ival * 2 ]
}
r=$(doubleCount)
echo "The New Value:" $r
执行,
$ ./test.sh
# Please Input A Value:200
# The New Value: 400
从上面的例子可以看到两点和以前不一样的地方:
- 函数
echo
语句的输出赋值给了接收函数返回值的变量 - 返回值可以大于256了,其实这种方式还可以返回浮点值和字符串值
# 3)函数参数
前面讲bash shell
会将函数当作小型脚本来对待。这意味着可以像普通脚本那样向函数传递参数,并可以用$1
和$2
这样获取传递给函数的参数值。
#!/bin/bash
function add {
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
echo -n "Adding 10 and 15: "
value=$(addem 10 15)
echo $value
echo -n "Try adding just one number: "
value=$(addem 10)
echo $value
echo -n "Trying adding no numbers: "
value=$(addem)
echo $value
echo -n "Try adding three numbers: "
value=$(addem 10 15 20)
echo $value
执行,
$ ./test.sh
# Adding 10 and 15: 25
# Try adding just one number: 20
# Trying adding no numbers: -1
# Try adding three numbers: -1
上面这个函数实例给出了如何在函数中获取参数,如何传递参数给函数。不过呢,由于在函数中使用$1
是获取传递给函数的参数,因此,函数中没有办法直接获取传给脚本的命令行参数,只能以函数参数的方式传递给函数。
# 4)函数变量
全局变量是在shell
脚本中任何地方都有效的变量。如果在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。
#!/bin/bash
function multiply2 {
v=$[ $v * 2 ]
}
read -p "Enter a value: " v
multiply2
echo "The New Value Is: $v"
执行,
$ ./test.sh
# Enter a value: 200
# The New Value Is: 400
很诧异吧,函数multiply2
中变量v
没有定义就可以使用,这是因为v
是全局变量,在调用multiply2
前定义了。这种方式很容易造成混乱,不建议过度使用。
函数内部使用的任何变量都可以被声明成局部变量,只要在变量声明的前面加上 local
关键字就可以了。
#!/bin/bash
function add5 {
local temp=$[ $value + 5 ]
result=$[ $temp * 2 ]
}
temp=4
value=6
add5
echo "The result is $result"
if [ $temp -gt $value ]
then
echo "temp is larger"
else
echo "temp is smaller"
fi
执行,
$ ./test.sh
# The result is 22
# temp is smaller
上面例子中有两点,
result
没有使用local
修饰,因此是全局变量local temp
限定了其作用域仅在函数中,不会影响函数外temp
的值
# 5)数组与函数
# 数组作为函数参数
将数组变量当作单个参数传递,它不会起作用。
#!/bin/bash
function test_array {
echo "The parameters are: $@"
thisarray=$1
echo "The received array is ${thisarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
test_array $myarray
执行,
$ ./test.sh
# The original array is: 1 2 3 4 5
# The parameters are: 1
# The received array is 1
可以看到通过$myarray
指定传入的只是数组的第一个元素。要解决这个问题,必须将该数组变量的值分解成单个的值,然后将这些值作为函数参数使用。
#!/bin/bash
function arraySum {
local sum=0
local newarray
newarray=($(echo "$@"))
for value in ${newarray[*]}
sum=$($sum + $value)
done
echo $sum
}
array=(1 2 3 4 5)
echo "The array is: ${array[*]}"
arg1=$(echo ${array[*]})
result=$(arraySum $arg1)
echo "The result is $result"
执行,
$ ./test.sh
# The array is: 1 2 3 4 5
# The result is 15
使用$(echo $value)
可以将echo
的输出保存到变量中,这样可以将数组传入到函数中。
# 数组作为函数的返回值
从函数中返回数组的操作也是使用echo
语句,其操作为:
#!/bin/bash
function multi2 {
local origarray
local newarray
local eles
local i
origarray=($(echo "$@"))
newarray=($(echo "$@"))
eles=$[ $# - 1 ]
for (( i = 0; i <= $eles; i++ ))
{
newarray[$i]=$[ ${origarray[$i]} * 2 ]
}
echo ${newarray[*]}
}
array=(1 2 3 4 5)
echo "Array is: ${array[*]}"
arg1=$(echo ${array[*]})
result=($(multi2 $arg1))
echo "The new array is: ${result[*]}"
执行,
$ ./test.sh
# Array is: 1 2 3 4 5
# The new array is: 2 4 6 8 10
注意,上面newarray=($(echo "$@"))
与newarray=$(echo "$@")
,如果没有()
,输出其实是一个整体,加上括号才是数组,不带括号,上面的脚本执行会报错。
# 6)创建shell
函数库
写一个脚本文件:
# compute文件
function addem {
echo $[ $1 + $2 ]
}
function multem {
echo $[ $1 * $2 ]
}
在需要使用该库文件的地方,只需要在脚本开头
source /xxx/xx/compute
然后就可以使用定义在compute
中的函数了。
source 命令有个快捷的别名,称作点操作符.
,还可以写成:
. /xxx/xx/compute
函数的应用绝不仅限于创建自己的函数自娱自乐。在开源世界中,可以下载大量各式各样的函数,并将其用于自己的应用程序中。譬如GNU shtool shell
脚本函数库其中提供了一些简单的shell
脚本函数,可以用来完成日常的shell
功能。