分享知识 传递快乐
1、函数定义
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。Shell 函数定义的语法格式如下:
[function] funname [()]{
函数体
[return int;]
}
格式1:
简化写法,不写 function 关键字:
函数名(){
函数体
}
格式2:
这是标准写法,也推荐大家使用的写法:
function 函数名(){
命令序列
}
这样方便做到一看就懂。
格式3:
如果写了 function 关键字,也可以省略函数名后面的小括号:
function 函数名{
命令序列
}
说明:
- function 是 Shell 中的关键字,专门用来定义函数;可以带function funname () 定义,也可以直接funname () 定义,不带任何参数;
- funname 是函数名;
- 函数体 是函数要执行的代码,也就是一组语句;
- return int 表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写,如果不加,将以最后一条命令运行结果,作为返回值。
2、函数变量
Shell 函数的变量不像其它语言中需要在变量前定义变量类型什么的,更没有结果符号等。语法:
num=1
变量作用域
局部变量:作用域是函数的生命周期;在函数结束时被自动销毁。定义局部变量的方法:
local VAR=VALUE
本地变量:作用域是运行脚本的shell进程的生命周期;因此,其作用范围为当前shell
示例一:
#!/bin/bash
#在函数外定义本地变量
var="Hello,World"
function show() {
#在函数内改变变量内容
var="Hi,var is changed"
}
echo "$var"
show
echo "$var"
输出结果:
[guest@localhost shell]$ ./tempsh.sh
Hello,World
Hi,var is changed
结果显示在调用函数后,原有的本地变量var被替换了。还好这个变量并不是重要的部分,想想若是PATH被替换了,那么这个函数的罪过就大了。因此我们如何即调用函数中定义的变量同时又不对本地变量造成任何影响呢?局部变量的出现就是为了解决这个问题。
下面看看在使用了局部变量后的效果。
#!/bin/bash
#在函数外定义本地变量
var="Hello,World"
function show() {
#在函数内改变变量内容
local var="Hi,var is changed"
echo "$var"
}
echo "$var"
show
echo "$var"
输出结果
[guest@localhost shell]$ ./tempsh.sh
Hello,World
Hi,var is changed
Hello,World
该实验结果说明,使用局部变量后,函数体中出现的变量作用范围只存在于当前函数生命周期。
3、函数调用
定义函数的代码段不会自动执行,而是在调用时执行;在函数定义好后,用户可以在shell 中直接调用,调用时不用带上();调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可。
示例一:
#!/bin/bash
# 函数定义
function show(){
echo "Hello word"
}
# 函数调用
show
输出结果:
Hello word
函数调用成功。上边的例子是把函数把在脚本上边,那么如果放函数放在下边会怎样呢?
无非就两种结果:1成功,2失败
下面我们举例测试一下:
示例二:
#!/bin/bash
# 函数调用
show
#函数定义
function show(){
echo "Hello word"
}
输出结果:
./tempsh.sh:行4: show: 未找到命令
系统报错,为啥?为什么会报错呢?
首先,脚本的执行顺序是从上到下顺序执行的,因此会先执行show,通过定义的环境变量$PATH定义的路径找不到show对应的命令因此报“show:未找到命令”。
我们在终端命令行中输错命令报错也是这个原因。终端命令行默认会将最左面输入的内容当做命令,因此若是错误的命令,不是命令的命令等内容都会报错。
通过上面的对比,我们至少知道函数的调用若是在同一个脚本中,调用操作需要在定义的函数后面。
4、函数传参
函数传参调用语法:
函数名 参数1 参数2 ....
如果传递参数,那么多个参数之间以空格分隔:
funname param1 param2 param3
不管是哪种形式,函数名字后面都不需要带括号。和其它编程语言不同的是,Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数。
在Shell中,调用函数时可以向其传递参数。在函数体内部,函数中的变量均为全局变量,没有局部变量,若使用局部变量需要在变量前加上 local,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数….
示例:
#!/bin/bash
function show(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
show 0 1 2 3 4 5 6 7 8 9 10 11
输出结果:
第一个参数为 0
第二个参数为 1
第十个参数为 10
第十个参数为 9
第十一个参数为 10
参数总数有 12 个
作为一个字符串输出所有参数 0 1 2 3 4 5 6 7 8 9 10 11
注意:$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
另外,还有几个特殊字符用来处理参数:
参数处理 | 说明 |
---|---|
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
5、函数返回值
退出状态码
在介绍函数返回值前先了解一下跟函数返回值有关的状态退出码。
Shell 中运行的每个命令都使用退出状态码(exit status)来告诉shell它完成了处理。退出状态码是一个0-255之间的整数值,在命令结束运行时由命令传给shell。你可以捕获这个值并在脚本中使用。
如何查看退出状态码呢?
Linux提供了 $? 专属变量来保存上个执行的命令的退出状态码。你必须在你要查看的命令之后马上查看或使用 $? 变量。它的值会变成Shell中执行的最后一条命令的退出状态码。
退出状态码大体分两种:
- 一种是命令正确执行的状态码,该状态码为:0
- 一种是命令错误执行的状态码,为1-255
Linux退出状态码
状态码 | 描述 |
0 | 命令成功结束 |
1 | 通用未知错误 |
2 | 误用shell命令 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效退出参数 |
128+x | Linux信号x的严重错误 |
130 | 命令通过Ctrl+C终止 |
255 | 退出状态码越界 |
在脚本中也可以指定退出状态码的值,通过命令exit实现。
状态码取值范围为0-255,如果在指定的状态码大于255,那么shell会通过模(模就是256)运算得到相应的退出状态码。
示例一:
成功的
#!/bin/bash
# 函数定义
function show(){
echo $(date +%Y%m%d)
}
show
echo $?
输出结果:
0
示例二:
失败的
#!/bin/bash
SYS_DATE=$(date +%Y%m%d)
echo $SYS_DATE
# 函数定义
function show(){
log=`lt`
echo log
}
show
echo $?
输出结果:
[guest@localhost shell]$ ./tempsh.sh
20191123
./tempsh.sh:行10: lt: 未找到命令
log
0
这次,由于函数最后一行命令正确执行,函数的退出状态码就是0,尽管函数中有一条命令没有成功运行。
使用函数的默认退出状态码是很危险的,幸运的是return命令可以解决这个问题。
示例:
#!/bin/bash
SYS_DATE=$(date +%Y%m%d)
echo $SYS_DATE
# 函数定义
function show(){
log=`lt`
echo log
return 2
}
show
echo $?
输出结果:
[guest@localhost shell]$ ./tempsh.sh
20191123
./tempsh.sh:行10: lt: 未找到命令
2
还是使用相同的函数,在函数最后加上return指定的状态码2。
函数返回值
Shell函数返回值,常用的两种方式:return、echo。
1)return 语句
Shell函数的返回值,可以和其他语言的返回值一样,通过return语句返回,return只能用来返回整数值。
示例一:
#!/bin/bash
function getResultFun(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}
getResultFun
echo $?
输出结果:
这是我的第一个 shell 函数!
2
Shell 函数返回值只能是整形数值,一般是用来表示函数执行成功与否的,0表示成功,其他值表示失败。用函数返回值来返回函数执行结果是不合适的。如果return某个计算结果,比如一个字符串,往往会得到错误提示:“numeric argument required”。
如果一定要让函数返回一个或多个值,可以定义全局变量,函数将计算结果赋给全局变量,然后脚本中其他地方通过访问全局变量,就可以获得那个函数“返回”的一个或多个执行结果了。
示例:
#!/bin/sh
function getStr(){
return "string"
}
getStr
echo $?
输出如下:
./test.sh: line 5: return: string: numeric argument required
255
可以看到已经提示要求return 整数类型,真实返回值是255。当面对这种问题怎么解决呢?
别急,断续看下去你就会找到你想要的答案了。
2)echo 语句
echo是通过输出到标准输出返回,可以返回任何类型的数据。
示例:
#!/bin/sh
function test() {
echo "arg1 = $1"
if [ $1 = "1" ] ;then
echo "1"
else
echo "0"
fi
}
echo
echo "test 1"
test 1
echo
echo "test 0"
test 0
echo
echo "test 2"
test 2
输出结果:
test 1
arg1 = 1
1
test 0
arg1 = 0
0
test 2
arg1 = 2
0
3)函数返回值总结
学习了上面的函数返回值的操作后我们下面做个知识总结,我们先看一用例:
#!/bin/bash
function getResultFun(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}
getResultFun
echo $?
function getResultFun2(){
echo "这是我的第二个 shell 函数!"
expr 1 + 1
}
getResultFun2
echo $?
getResultFun
echo 在这里插入命令!
echo $?
输出结果:
这是我的第一个 shell 函数!
2
这是我的第二个 shell 函数!
2
0
这是我的第一个 shell 函数!
在这里插入命令!
0
这是为什么?
因为调用 getResultFun2 后,函数最后一条命令 expr 1 + 1 得到的返回值($?值)为 0,意思是这个命令没有出错。所有的命令的返回值仅表示其是否出错,而不会有其他有含义的结果。
第二次调用 getResultFun 后,没有立即查看 $? 的值,而是先插入了一条别的 echo 命令,最后再查看 $? 的值得到的是 0,也就是上一条 echo 命令的结果,而 getResultFun 的返回值被覆盖了。下面这个测试,连续使用两次 echo $?,得到的结果不同,更为直观:
#!/bin/bash
function getResult(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}
getResult
echo $?
echo $?
输出结果:
这是我的第一个 shell 函数!
2
0
6、获取函数返回值
我们上面谈到了函数定义、传参、调用、返回结果,那我们如果得到返回结果呢?上面也谈到了一种获取返回结果的方法 $? ,难道只有这一种方式吗?答案肯定不止。
示例一:
用 $? 获取返回值,上面已有介绍,在这里就不做介绍了。
示例二:
#!/bin/sh
function getStr(){
return "string"
}
#方法一
echo `getStr`
#方法二
echo $(getStr)
两种写法的原理一样的,将getStr当成命令执行,然后获取其标准输出。
示例三:函数传参
#!/bin/bash
#创建目录
function createDir(){
if [ ! -d $1 ]; then
mkdir -p $1
fi
}
DIR="temp/"
# 两者二先一
#$(createDir $DIR)
$(createDir "temp/")
函数返回值获取的方法总结:
- 用变量接收函数返回值,函数用echo等标准输出将要返回的东西打印出来。
- 用 $? 来接收函数的执行状态,但是 $? 要紧跟在函数调用处的后面。
———————————
相互学习,共同进步
如有不足请留言指正