在shell脚本编写中,通常有很多处使用相同的代码。如果这些代码比较短小,name通常并不费事。但是,如果有打断代码需要重写,则会让人厌烦,这个我们就需要使用函数来处理;
1、基本脚本函数
函数是被富裕名称的脚本代码块,可以在代码的任意位置重复使用。引用函数的过程,我们称之为调用;
1.1、创建函数
在bash shell脚本中创建函数可以使用两种格式:
function name { #name为唯一名称 commands #commands为组成函数功能的命令 }
另外一种格式
name() {
commands
}
1.2、使用函数
相应的函数名需要在脚本中指定
#!/bin/bash # using a function in a script function func1 { echo "This is an example of a funcation" } 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"
注意,不能在函数被定义之前去调用这个函数,会报错
#!/bin/bash # using a function located in the middle of a script count=1 echo "This line comes before the function definition" function func1 { echo "This is an example of a function" } while [ $count -le 5 ] do func1 count=$[ $count + 1 ] done echo "This is the end of the loop" func2 #没有定义func2,所以这里会报错 echo "Now this is the end of the script"
函数的命名也需要注意,必须唯一。如果重新定义了函数,则新定义的函数就会覆盖原来的函数
#!/bin/bash # testing using a duplicate function name function func1 { echo "This is first definition of the function name" } func1 function func1 { #新定义的函数就会覆盖原来的函数 echo "This is a repeat of the same function name" } func1 echo "This is the end of the script"
2、返回值
bash shell将函数看成小型的脚本,并以退出转态结束。函数退出转态有三种生成方式;
2.1、默认退出状态
函数的退出转态是函数的最后一条命令返回的退出状态 。可以$?来确定函数的退出状态;
#!/bin/bash # testing the exit status of a function func1 () { echo "Trying to display a non-existend file" ls -l badfile } echo "testing the function:" func1 echo "The exit status is: $?"
这样可以知道函数执行转态为1,但是无法知道其他命令是否执行成功。
#!/bin/bash # testing the exit status of a function func1 () { ls -l badfile echo "This was a test of a bad command" } echo "testing the functiong:" func1 echo "The exit status is: $?"
因为函数中命令位置变换,则返回的函数转态值就是0了;所以这种方式来判断函数是不正确的;
2.2、使用return命令
所以,我们引入了return命令来指定函数转态。return命令可以使用单个整数值来定义函数退出转态,提供了一种通过编辑设置函数退出转态的简单方法;
#!/bin/bash # using the return command in a function function db1 { read -p "Enter a value: " value echo "doubling the value" return $[ $value * 2 ] #db1函数将变量$value中的用户输入值变为双倍后,通过return返回 } db1 echo "The new value is $?"
注意两点:1、函数完成后尽快提取返回值;2、退出转态的取值范围是0-255;
2.3、使用函数输出
函数输出的结果可以给变量赋值:
result=`db1`
这样db1函数的值,就给了$result中
#!/bin/bash # using the echo to return a value function db1 { read -p "Enter a value: " value echo $[ $value * 2 ] } result=`db1` echo "The new value is $result"
这个函数使用echo语句显示计算结果,获取了db1的输出,而不是查看命令运行的状态结果;
3、在函数中使用变量
上一个函数中的$value就是函数变量使用的方法之一,在函数中要注意变量调用;
3.1、向函数传递参数
$1 $2 $# 如何传递给函数呢?
#!/bin/bash # passing parameters to a function function addem { 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 "Let's try adding just one number: " value=`addem 10` echo $value echo -n "Now trying adding no numbers: " value=`addem` echo $value echo -n "Finally, try adding three numbers: " value=`addem 10 15 20` echo $value
由于函数为自己的参数值使用专用的参数环境变量,所以函数无法从脚本命令行直接范文脚本参数值。下面的例子就是一个错误的例子:
#!/bin/bash # trying to access script parameters inside a function function badfunc1 { echo $[ $1 * $2 ] } if [ $# -eq 2 ] then value=`badfunc1` echo "The result is $value" else echo "Usage: badtest1 a b" fi
./badtest1 10 15
./badtest1: * : syntax error: operand expected (error token is "*")
该函数使用管道变量$1和$2,不同于脚本主代码的变量$1和$2.如果想在函数中使用这些值,那么必须在调用该函数时手动传递这些数据:
#!/bin/bash # trying to access script parameters inside a function 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
3.2、在函数中处理变量
函数中使用两种变量:1、全局变量;2、局部变量;
1、全局变量
shell脚本中全部地方都会生效
#!/bin/bash # using a global variable to pass avalue function db1 { value=$[ $value * 2 ] } read -p "Enter a value: " value db1 echo "The new value is: $value"
$value在函数外部复制,同时在函数内部使用,依然生效。
郑重做法要求程序员确切的清除函数中使用了那些变量,包括那些勇于计算值且不返回脚本的所有变量。
#!/bin/bash # demonstrating a bad use of variables function func1 { 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
这种情况下,temp又在函数外部赋值,又在行数内部使用,所以结果可能会出人意料
2、局部变量
local 代表局部变量:local temp
local temp=$[ $value + 5 ]
local变量仅仅在函数内部使用;
#!/bin/bash # demonstrating the local keyword 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
这样,func1内部使用变量$temp时,脚本主代码变量$temp的值不会受到映像
4、数组变量与函数
函数使用数组变量值需要一些技巧。
4.1、向函数传递数组
如果试图将数组变量作为单个参数传递,是无法正常工作的:
#!/bin/bash # trying to pass an array variable function testit { echo "The parameters are: $@" this array=$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[*]}
这时候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=`The original array is: ${myarray[*]}` result=`addarray $arg1` echo "The result is $result"
4.2、从函数返回数组
使用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[*]}"
./test12 The original array is: 1 2 3 4 5 The new array is: 2 4 6 8 10
5、函数递归
递归调用函数是指函数调用自身进行求解。递归的一个经典案例是计算阶乘;
5!=1*2*3*4*5=120
这个就可以显示为:x!=x*(x-1)!
这就可以以脚本的形式展现出来:
function factorial { if [ $1 -eq 1 ] then echo 1 else local temp=$[ $1 - 1 ] local result=`factorial $temp` echo $[ $result * $1 ] fi }
该阶乘函数调用自身进行计算:
#!/bin/bash # using recursion 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"
6、创建库
多个脚本使用同样的函数,这时候我们就需要创建库文件才行;
# my script functions function addem { echo $[ $1 + $2 ] } function multem { echo $[ $1 + $2 ] } function divem { if [ $2 -ne 0 ] then echo $[ $1 / $2 ] else echo -1 fi }
下一步将这个文件包含进需要调研的库函数中
问题:如果从shell命令行界面运行myfuncs脚本,nameshell将打开一个新shell,并在该新shell中运行脚本。这将为新shell定义3个函数,但是当试图运行调用这些函数的另一个脚本时,库函数并不使用;
#!/bin/bash # using a library file the wrong way ./myfuncs result=`addem 10 50` echo "The result is $result"
使用函数库的关键命令时source命令。使用source命令再shell脚本内部运行库文件脚本。这样脚本就可以使用这些函数。
#!/bin/bash # using functions defined in a library file . ./myfuncs value1=10 value2=5 result1=`addem $value1 $value2` result2=`multem $value1 $value2` result3=`divem $value1 $value2` echo "The result of adding them is: $result1" echo "The result of multiplying them is: $result2" echo "The result of dividing them is: $result3"
7、在命令行中使用函数
函数可以使用在外面命令行中
7.1、在命令行创建函数
两种方法:
1、将函数定义在一行命令中:
$ function divem { echo $[ $1 / $2 ]; }
$ divem 100 5
在命令行中定义函数,每条命令后面必须用分号隔开;
$ funtion doubleit { read -p "Enter value: " value; echo $[ $value * 2 ]; }
$ doubleit
2、使用多行命令定义函数。这样,bash shell使用次级命令提示符输入更多的命令
$ function multem {
> echo $[ $1 * $2 ]
> }
$ multem 2 5
10
$
7.2、在.bashrc文件中定义函数
在命令行下定义函数,一退出,就不能继续运行了。所以我们可以在.bashrc文件中来定义;
1、直接定义函数
$ cat .bashrc # .bashrc # Source global definitions if [ -r /etc/bashrc ]; then . /etc/bashrc fi function addem { echo $[ $1 + $2 ] }
2、提供函数文件
正如shell脚本中的做法一样,可以使用source命令(点操作符),将库文件的函数包含进.bashrc脚本:
$ cat .bashrc # .bashrc # Source global definitions if [ -r /etc/bashrc ]; then . /etc/bashrc fi . /home/rich/libraries/myfuncs
确保库文件的准确路径,这样bash shell才能够找到库文件。再次启动shell之后,该库的所有函数将都能在命令行界面使用;
更好的是,shell还将全部已定义函数传递给子shell进程。这样从shell会话运行的脚本,自定义函数随之可用。
#!/bin/bash # using a function defined in the .bashrc file value1=10 value2=5 result1=`addem $value1 $value2` result2=`multem $value1 $value2` result3=`divem $value1 $value2` echo "The result of adding them is: $result1" echo "The result of multiplying them is: $result2" echo "The result of dividing them is: $result3"