在shell脚本编写中,通常有很多处使用相同的代码。如果这些代码比较短小,name通常并不费事。但是,如果有打断代码需要重写,则会让人厌烦,这个我们就需要使用函数来处理;
1、基本脚本函数
函数是被富裕名称的脚本代码块,可以在代码的任意位置重复使用。引用函数的过程,我们称之为调用;
1.1、创建函数
在bash shell脚本中创建函数可以使用两种格式:
function name { #name为唯一名称 commands #commands为组成函数功能的命令}
另外一种格式
name() {commands}
1.2、使用函数
相应的函数名需要在脚本中指定
#!/bin/bash# using a function in a scriptfunction func1 { echo "This is an example of a funcation"}count=1while [ $count -le 5 ]do func1 count=$[ $count + 1 ]doneecho "This is the end of the loop"func1echo "Now this is the end of the script"
注意,不能在函数被定义之前去调用这个函数,会报错
#!/bin/bash# using a function located in the middle of a scriptcount=1echo "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 ]doneecho "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 namefunction func1 { echo "This is first definition of the function name"}func1function func1 { #新定义的函数就会覆盖原来的函数 echo "This is a repeat of the same function name"}func1echo "This is the end of the script"
2、返回值
bash shell将函数看成小型的脚本,并以退出转态结束。函数退出转态有三种生成方式;
2.1、默认退出状态
函数的退出转态是函数的最后一条命令返回的退出状态 。可以$?来确定函数的退出状态;
#!/bin/bash# testing the exit status of a functionfunc1 () { echo "Trying to display a non-existend file" ls -l badfile}echo "testing the function:"func1echo "The exit status is: $?"
这样可以知道函数执行转态为1,但是无法知道其他命令是否执行成功。
#!/bin/bash# testing the exit status of a functionfunc1 () { ls -l badfile echo "This was a test of a bad command"}echo "testing the functiong:"func1echo "The exit status is: $?"
因为函数中命令位置变换,则返回的函数转态值就是0了;所以这种方式来判断函数是不正确的;
2.2、使用return命令
所以,我们引入了return命令来指定函数转态。return命令可以使用单个整数值来定义函数退出转态,提供了一种通过编辑设置函数退出转态的简单方法;
#!/bin/bash# using the return command in a functionfunction db1 { read -p "Enter a value: " value echo "doubling the value" return $[ $value * 2 ] #db1函数将变量$value中的用户输入值变为双倍后,通过return返回}db1echo "The new value is $?"
注意两点:1、函数完成后尽快提取返回值;2、退出转态的取值范围是0-255;
2.3、使用函数输出
函数输出的结果可以给变量赋值:
result=`db1`
这样db1函数的值,就给了$result中
#!/bin/bash# using the echo to return a valuefunction 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 functionfunction 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 $valueecho -n "Let's try adding just one number: "value=`addem 10`echo $valueecho -n "Now trying adding no numbers: "value=`addem`echo $valueecho -n "Finally, try adding three numbers: "value=`addem 10 15 20`echo $value
由于函数为自己的参数值使用专用的参数环境变量,所以函数无法从脚本命令行直接范文脚本参数值。下面的例子就是一个错误的例子:
#!/bin/bash# trying to access script parameters inside a functionfunction 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 functionfunction 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 avaluefunction db1 { value=$[ $value * 2 ]}read -p "Enter a value: " valuedb1echo "The new value is: $value"
$value在函数外部复制,同时在函数内部使用,依然生效。
郑重做法要求程序员确切的清除函数中使用了那些变量,包括那些勇于计算值且不返回脚本的所有变量。
#!/bin/bash# demonstrating a bad use of variablesfunction func1 { temp=$[ $value + 5 ] result=$[ $temp * 2 ]}temp=4value=6func1echo "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 keywordfunction func1 { local temp=$[ $value + 5 ] result=$[ $temp * 2 ]}temp=4value=6func1echo "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 variablefunction 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 testfunction 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 arrayfunction 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 valuefunction 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[*]}"
./test12The original array is: 1 2 3 4 5The 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 recursionfunction 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: " valueresult=`factorial $value`echo "The factorial of $value is: $result"
6、创建库
多个脚本使用同样的函数,这时候我们就需要创建库文件才行;
# my script functionsfunction 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./myfuncsresult=`addem 10 50`echo "The result is $result"
使用函数库的关键命令时source命令。使用source命令再shell脚本内部运行库文件脚本。这样脚本就可以使用这些函数。
#!/bin/bash# using functions defined in a library file. ./myfuncsvalue1=10value2=5result1=`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 definitionsif [ -r /etc/bashrc ]; then . /etc/bashrcfifunction addem {echo $[ $1 + $2 ]}
2、提供函数文件
正如shell脚本中的做法一样,可以使用source命令(点操作符),将库文件的函数包含进.bashrc脚本:
$ cat .bashrc# .bashrc# Source global definitionsif [ -r /etc/bashrc ]; then . /etc/bashrcfi. /home/rich/libraries/myfuncs
确保库文件的准确路径,这样bash shell才能够找到库文件。再次启动shell之后,该库的所有函数将都能在命令行界面使用;
更好的是,shell还将全部已定义函数传递给子shell进程。这样从shell会话运行的脚本,自定义函数随之可用。
#!/bin/bash# using a function defined in the .bashrc filevalue1=10value2=5result1=`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"