01. 基本的函数脚本
01. 创建函数
函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了(这个过程称为调用函数)。
name属性定义了赋予函数的唯一名称。脚本中定义的每个函数都必须有一个唯一的名称。下边是两种创建函数的方式。
function name {
commands
}
name() {
commands
}
02. 使用函数
必须先定义函数,再调用函数
#!/bin/bash
# using a function in a script
function func1 {
echo "This is an example of a function"
}
count=1
while [ $count -le 5 ]
do
func1
count=$[ $count + 1 ]
done
echo "This is the end of the loop"
func1
echo "Now this is the end of the script"
如果你重定义了函数,新定义会覆盖原来函数的定义,这一切不会产生任何错误消息。
函数定义不一定非得是shell脚本中首先要做的事(但必须先定义后调用,也可以定义到函数库中,方便脚本调用)。
02. 返回值
01. 默认退出状态码
默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量 $? 来确定函数的退出状态码。
#!/bin/bash
# testing the exit status of a function
func1() {
echo "trying to display a non-existent file"
ls -l badfile
}
echo "testing the function: "
func1
echo "The exit status is: $?"
缺陷:无法判断整个函数中的命令是否都执行成功了,使用函数的默认退出状态码是很危险的。
02. 使用return 命令
使用return命令来退出函数并返回特定的退出状态码。return命令允许指定一个整数值来定义函数的退出状态码,从而提供了一种简单的途径来编程设定函数退出状态码。
#!/bin/bash
# using the return command in a function
function dbl {
read -p "Enter a value: " value
echo "doubling the value"
return $[ $value * 2 ]
}
dbl
echo "The new value is $?"
特别需要注意两点,否则会报错:
- 记住,函数一结束就取返回值
- 记住,退出状态码必须是0~255
03. 使用函数输出
正如可以将命令的输出保存到shell变量中一样,你也可以对函数的输出采用同样的处理办法。可以用这种技术来获得任何类型的函数输出,并将其保存到变量中:
#!/bin/bash
# using the echo to return a value
function dbl {
read -p "Enter a value: " value
# 函数输出,使用echo
echo $[ $value * 2 ]
}
# 这个命令会将dbl函数的输出赋给$result变量。
result=$(dbl)
echo "The new value is $result"
说明:通过这种技术,你还可以返回浮点值和字符串值。这使它成为一种获取函数返回值的强大方法。
03. 在函数中使用变量
01. 向函数传递参数
函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在 $0 变量中定义,函数命令行上的任何参数都会通过 $1、$2 等定义。也可以用特殊变量 $# 来判断传给函数的参数数目。
函数可以用参数环境变量来获得参数值
function addem {
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
value=$(addem 10 15 20)
echo $value
由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值(函数参数与脚本参数使用了相同的特殊变量,这种情况不能并存)。
尽管函数也使用了 $1 和 $2 变量,但它们和脚本主体中的 $1 和 $2 变量并不相同。要在函数中使用这些值,必须在调用函数时手动将它们传过去。
function func7 {
echo $[ $1 * $2 ]
}
if [ $# -eq 2 ]
then
value=$(func7 $1 $2)
echo "The result is $value"
else
echo "Usage: badtest1 a b"
fi
02. 在函数中处理变量
函数中定义的变量与普通变量的作用域不同。也就是说,对脚本的其他部分而言,它们是隐藏的。
全局变量
全局变量是在shell脚本中任何地方都有效的变量。如果你在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚本的主体部分读取它的值。
默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问。
function dbl {
value=$[ $value * 2 ]
}
read -p "Enter a value: " value
dbl
echo "The new value is: $value"
$value变量在函数外定义并被赋值。当dbl函数被调用时,该变量及其值在函数中都依然有效。如果变量在函数内被赋予了新值,那么在脚本中引用该变量时,新值也依然有效。
局部变量
如果你想在不同的shell脚本中使用函数的话。它要求你清清楚楚地知道函数中具体使用了哪些变量,包括那些用来计算非返回值的变量。
无需在函数中使用全局变量,函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,只要在变量声明的前面加上local关键字就可以了。
local temp
local temp=$[ $value + 5 ]
local关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,那么shell将会保持这两个变量的值是分离的。现在你就能很轻松地将函数变量和脚本变量隔离开了,只共享需要共享的变量
function func1 {
# 局部变量
local temp=$[ $value + 5 ]
# 全局变量
result=$[ $temp * 2 ]
}
temp=4
value=6
func1
echo "The result is $result"
if [ $temp -gt $value ]
then
echo "temp is larger"
else
echo "temp is smaller"
fi
04. 数组变量和函数
01. 向函数传数组参数
将数组变量当作单个参数传递的话,它不会起作用。
#!/bin/bash
# trying to pass an array variable
function testit {
echo "The parameters are: $@"
thisarray=$1
echo "The received array is ${thisarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
testit $myarray
如果你试图将该数组变量作为函数参数,函数只会取数组变量的第一个值。要解决这个问题,你必须将该数组变量的值分解成单个的值,然后将这些值作为函数参数使用。在函数内部,可以将所有的参数重新组合成一个新的变量。
#!/bin/bash
# array variable to function test
function testit {
local newarray
# 在函数内部重建数组变量
newarray=(;'echo "$@"')
echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
# 将函数的数组参数当做多个单词传入
testit ${myarray[*]}
传入函数内部后,数组仍然可以正常使用。
!/bin/bash
# adding values in an array
function addarray {
local sum=0
local newarray
# 重建数组变量
newarray=($(echo "$@"))
# 遍历累加数组值
for value in ${newarray[*]}
do
sum=$[ $sum + $value ]
done
echo $sum
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
# 将数组转化成一个大字符串
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "The result is $result"
02. 从函数返回数组
函数用echo语句来按正确顺序输出单个数组值,然后脚本再将它们重新放进一个新的数组变量中。
#!/bin/bash
# returning an array value
function arraydblr {
local origarray
local newarray
local elements
local i
origarray=($(echo "$@"))
newarray=($(echo "$@"))
elements=$[ $# - 1 ]
for (( i = 0; i <= $elements; i++ ))
{
newarray[$i]=$[ ${origarray[$i]} * 2 ]
}
# 返回数组列表
echo ${newarray[*]}
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
# 整合成一个大字符串
arg1=$(echo ${myarray[*]})
# 函数返回列表,再次重建成一个新数组
result=($(arraydblr $arg1))
echo "The new array is: ${result[*]}"
05. 递归函数
递归算法的经典例子是计算阶乘。一个数的阶乘是该数之前的所有数乘以该数的值。因此,要计算5的阶乘,可以执行如下方程:
5! = 1 * 2 * 3 * 4 * 5 = 120
使用递归,方程可以简化成以下形式:
x! = x * (x-1)!
也就是说, x的阶乘等于x乘以x-1的阶乘。这可以用简单的递归脚本表达为:
# 每递归一次,x!的乘积形式展开一次,直到全部展开为乘积形式
function factorial {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[ $1 - 1 ]
# 调用自己本身
local result='factorial $temp'
echo $[ $result * $1 ]
fi
}
read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
06. 创建函数库
如果你碰巧要在多个脚本中使用同一段代码呢?bash shell允许创建函数库文件,然后在多个脚本中引用该库文件。
#!/bin/bash
# using functions defined in a library file
# 加载函数库文件,当前目录下的 ./myfuncs
. ./myfuncs
#
......
#
使用函数库的关键在于source命令。 source命令会在当前shell上下文中执行命令,而不是创建一个新shell。可以用source命令来在shell脚本中运行库文件脚本。这样脚本就可以使用库中的函数了。source命令还有个快捷的别名,称作点操作符。
07. 在命令行上使用函数
01. 在命令行上创建函数
当在命令行上定义函数时,你必须记得在每个命令后面加个分号,这样shell就能知道在哪里是命令的起止了。
function doubleit { read -p "Enter value: " value; echo $[ $value * 2 ]; }
另一种方法是采用多行方式来定义函数。在定义时, bash shell会使用次提示符来提示输入更多命令。用这种方法,你不用在每条命令的末尾放一个分号,只要按下回车键就行。
$ function multem {
> echo $[ $1 * $2 ]
> }
02. 在.bashrc 文件中定义函数
在命令行上直接定义shell函数的明显缺点是退出shell时,函数就消失了。一个非常简单的方法是将函数定义在一个特定的位置,这个位置在每次启动一个新shell的时候,都会由shell重新载入。最佳地点就是 .bashrc 文件。
- 直接在文件中定义函数
- 也可以直接加载函数库文件