Shell函数是一组命令集或语句组成一个可用块。利用函数可以简化脚本编写。函数要求先定义再使用,调用函数时直接使用函数名即可。这里主要介绍shell编程中函数定义、调用、获取函数参数以及获取函数返回值等内容。
函数定义
函数由函数名和函数体两部分组成。形式如下:
function_name()
{
程序段
}
也可以在函数名前面加上function关键字。如下
function function_name() { 程序段 }
下面我们看一个shell函数最简单的实例,打印指定输出内容:
print_msg() <-- 定义函数,函数名为print_msg { echo "Hello World" <-- 函数体 }
这里,print_msg就是函数名,echo行就是函数体。函数定义就是这样子,那么如何调用函数呢?就是通过使用函数名+参数的方法进行函数调用。函数调用语法如下:
function_name [para1] [para2] ... [paraN]
由于print_msg()函数不需要参数,所以调用形式如下:
#!/bin/bash print_msg() #定义函数,函数名为print_msg { echo "Hello World" #函数体 } print_msg #调用print_msg函数
到这里,函数的定义和函数调用方法就介绍完了。但是如果你想要函数打印你指定输出内容呢?这时就需要考虑向函数传递指定参数了。
向函数传递参数
我们先将上述指定输出固定内容的print_msg函数进行扩展,实现按照指定内容输出的函数。代码如下:
#!/bin/bash print_msg() # 定义函数,函数名为prompt_msg { msg_level=$1 # 获取函数输入的第一个参数,赋值给变量msg_level msg_info=$2 #获取函数输入的第二个参数,赋值给变量msg_info echo "${msg_level}: ${msg_info}" } print_msg "ERROR" "The Directory not exist" #使用函数名prompt_msg调用函数,向函数传递2个参数,分别为"Error"、"The Directory not exist"
这里,我们注意到函数体中出现了$1、$2的特殊字符。这代表什么含义?这里也是本章节想要描述的主要内容。下面我们看下函数中常用的类似$1、$2的特殊字符以及它们的含义和使用方法。
$0:表示调用的脚本名。
$1、$2、$3...$n:表示传入函数的第1个参数、第2个参数、第3个参数...第n个参数。
$#:表示传入参数个数的总数。
$@:表示"$1"、"$2"、"$3"..."$n",每个变量都是独立的,常用。
$*:表示"$1 $2 $3...$n",每个变量不是独立的。如果以$*作为传参相当于就只有1个参数。
说明:$@要比$*用的多,因为$*将所有的参数当做单个字符串,因此它很少被使用。
下面我们就针对$0、$1、$#、$@、$*等特殊符号进行逐一介绍。利用这些符号,我们继续完善上述的例子。
#!/bin/bash print_msg() #定义函数,函数名为prompt_msg { if [ $# -ne 2 ];then # $#表示传入参数总数,即如果参数总数不等于2,则函数报错退出 echo "$0" #输出函数名 echo "$@" # $#输出所有传入函数的参数 echo "please input 2 parameters,please check" exit 1 fi msg_level=$1 #获取函数输入的第一个参数,赋值给变量msg_level msg_info=$2 #获取函数输入的第二个参数,赋值给变量msg_info echo "${msg_level}: ${msg_info}" } print_msg "Para1" "Para2" "Para3" # 使用函数名prompt_msg调用函数,输入"Para1" "Para2" "Para3" 3个参数
输出结果如下:
./print_msg.sh #$0 脚本名 Para1 Para2 Para3 # $@ please input 2 parameters,please check
从输出结果应该知道$0、$#、$@表示的含义了。那$@和$*在使用上有什么区别呢?我们通过2个实例来看下,假设脚本名称为script.sh。代码如下
#!/bin/bash main() { echo "Parameter number: $#" echo "Parameter Value: $@" } main $@
调用脚本为:script.sh 'Hello' 'World',输出结果如下
Parameter number: 2 Parameter Value: Hello World
说明:main $@就相当于main ‘Hello’ ‘World’,独立的输入传入脚本的参数。
使用函数返回值
通常使用函数完成一段逻辑处理或者判断函数返回状态完成下一步处理。函数返回值通常有2种形式:(1)使用return语句返回某个数值(0~255),0通常用于表示正确处理无错误返回,非0表示处理异常返回。(2)使用echo返回指定字符串,然后调用程序将函数的执行结果赋值给指定变量。
(1)使用return返回数值的使用方法。下面我们通过一个判断是否为奇数的函数来先了解下,代码如下:
#!/bin/bash is_odd() { num=$1 if [ $(expr $num % 2) -ne 0 ];then return 0 else return 1 fi } number=3 is_odd "${number}" if [ $? -eq 0 ];then # $?表示上一条命令执行返回的。 echo "$number is odd" else echo "$number is not odd" fi
说明:is_odd函数使用return语句返回判断结果。下一个命令通过$?获取函数的输出返回值进行判断。记住$?的使用方法,非常常用。
(2)使用echo返回字符串的方法,下面我们通过一个简单的例子来看下。
#!/bin/bash fun() { echo "Hello World" } Str=$(fun) echo $Str
函数利用echo返回"Hello Word"字符串,脚本再通过变量Str去获取即可。就是这么使用。
函数变量和脚本变量
所谓的脚本变量就是不在函数内定义的变量。使用函数变量和脚本变量主要关注其作用域。二者的作用域不同,主要区别如下:
(1)脚本变量是全局的,作用域是从变量定义到脚本结束。
#!/bin/bash func() { echo $num #输出100 num=200 # 修改变量num值为200 } num=100 func # 调用函数,num值修改为200 echo $num # 输出200
(2)函数变量定义的变量默认也是全局的,但是作用域是从函数被调用时执行变量定义开始到脚本结束。
#!/bin/bash func() { num=200 # 定义函数变量num,默认属性是全局。 } func echo $num # 输出200
(3)如果函数变量前带关键字local,则函数变量作用域仅局限于函数内。
#!/bin/bash func() { local num=200 # 带关键字local,定义函数变量num。num变量作用域为函数内。 echo $num # 输出200 } num=100 func echo $num # 输出100,变量值未改变。如果把num=100的定义去掉,这里会显示为空,原因是变量未定义。
看了这3个例子,应该清楚函数变量和脚本变量的作用域区别。在shell编程中,建议在函数中定义变量全部带关键字local,否则容易出现全局变量被修改而导致程序执行错误。
参数变量偏移shift
参数变量偏移shift用的比较少,但是不代表不需要会用,某些使用利用shift可以很快速的完成指定的需求。刚开始学习shell编程的时候一直不太理解这个shift关键字有什么作用,也不知道在哪些场景下可以使用,后来在阅读别人的代码和实践中才发现其妙处。下面我们先看例子,通过例子再介绍它的使用方法。假设脚本名为script.sh,脚本调用传参为script.sh "add" "100" "200" "300"
#!/bin/bash add() { echo "$1+$2+$3"|bc } multiply() { echo "$1*$2*$3"|bc } if [ $1 == "add" ];then echo "$@" <-- 打印脚本传入的所有参数,输出add 100 200 300 shift <-- 偏移第1个参数 echo "$@" <-- 打印便宜后的参数,输出100 200 300 add $@ <-- 获取相加的值,输出600 else shift multiply $@ fi
看结果应该知道shift用法了吧。shift会移动参数变量,而且shift后面可以接数字,代表跳过前面几个参数。也就是当你脚本考虑跳过某些参数时,就可以使用shift关键字了。
总结
使用函数可以节省大量的脚本编写时间,也可以使脚本结构更加清晰。对于经常使用到的逻辑语句可以整理成函数保存到文件中,可以方便后续脚本编写。