首页 > 代码库 > Linux基础之bash脚本进阶篇-函数

Linux基础之bash脚本进阶篇-函数

函数,什么是函数?

函数的出现最初是在数学中,它的数学定义如下:在某变化过程中有两个变量x,y,按照某个对应法则,对于给定的x,有唯一确定的值y与之对应,那么y就叫做x的函数。

而在计算机中函数的内涵发生了一些变化。

在编程中,为了简化代码量,通常会将经常调用的一些代码模块化,并一一个名字表示,当再次使用该模块时只需要输入该名字,系统会自动去读取该名字所对应的代码模块。因此在计算机中把一段独立功能的代码当做一个整体,并为之命一个名字,命名的代码段即为函数

虽然此函数非彼函数但函数最本质的意义并未改变:按照某个对应法则的对应关系



函数的语法格式

    格式1:

    function function_name() {

       ... 函数体

    }

    格式2:

    function_name () {

       ... 函数体

    }



函数的调用

注:定义函数的代码段不会自动执行在调用时执行;调用即在代码中给定函数并即可;函数名出现的任何位置,在代码执行时,都会被自动替换为函数代码。

示例:定义一sayhello函数,输出“Hello,World!”

#!/bin/bash
#function sayhello
#author chawan
#定义函数
function sayhello () {
echo "Hello,World!"
}
#调用函数
sayhello

运行脚本

[root@docker test]# sh 20160909-1
Hello,World!

函数调用成功。

这时候有个疑惑:上面的脚本,如果我们将调用函数操作放在定义函数的前面会发生怎样的情况呢?

两种情况:1、调用失败2、正常调用

下面通过实验来测试:

#!/bin/bash
#function sayhello
#author chawan
#调用函数
sayhello
#定义函数
function sayhello () {
echo "Hello,World!"
}

运行脚本

[root@docker test]# sh 20160909-1
20160909-1:行5: sayhello: 未找到命令

系统报错,为什么报错呢

首先,脚本的执行总体上是顺序执行,因此会先执行sayhell,通过定义的环境变量$PATH定义的路径找不到sayhello对应的命令因此报“未发现sayhello命令”。

我们在终端命令行中输错命令报错也是这个原因。终端命令行默认会将最左面输入的内容当做命令,因此若是错误的命令,不是命令的命令等内容都会报错。

通过上面的对比,我们至少知道函数的调用若是在同一个脚本中,调用操作需要在定义的函数后面



函数的链接

所谓函数链接:是指在某个shell函数中调用另外一个函数的过程。

shell允许用户函数的嵌套使用

示例:演示某个函数中同时调用多个其他函数。

#!/bin/bash
#函数间的调用
#author chawan date:20160909
john() {
echo "Hello,John!"
}
tom() {
john
echo "Hello,tom!"
}
sayhello() {
tom
lilei
}
lilei() {
echo "Hello,lilei!"
}
sayhello

运行脚本,结果如下:

[root@docker test]# sh 20160909-2
Hello,John!
Hello,tom!
Hello,lilei!

这个脚本我故意将lilei函数放在sayhello函数后面,结果sayhello正常调用lilei,所以只要调用函数的位置在当前脚本所设定的函数之后即不受函数顺序的影响。函数之间无顺序制约



函数返回值

在介绍函数返回值前先讲述下跟函数返回值有关的状态退出码

状态退出码

shell中运行的每个命令都使用退出状态码(exit status)来告诉shell它完成了处理。退出状态码是一个0-255之间的整数值,在命令结束运行时由命令传给shell。你可以捕获这个值并在脚本中使用。

查看退出状态码

Linux提供了$?专属变量来保存上个执行的命令的退出状态码。你必须在你要查看的命令之后马上查看或使用$?变量。它的值会变成shell中执行的最后一条命令的退出状态码:

示例:查看命令状态码

[root@docker test]# ls /etc >> /dev/null
[root@docker test]# echo $?
0
[root@docker test]# basdc
bash: basdc: 未找到命令...
[root@docker test]# echo $?
127

退出状态码大体分两种:

一种是命令正确执行的状态码,该状态码为:0

一种是命令错误执行的状态码,为1-255

                            Linux退出状态码

状态码描述
0命令成功结束
1通用未知错误
2误用shell命令
126命令不可执行
127没找到命令
128无效退出参数
128+xLinux信号x的严重错误
130命令通过Ctrl+C终止
255退出状态码越界

在脚本中可以指定退出状态码的值,通过命令exit实现

示例:指定退出状态码

#!/bin/bash
#exit status
echo "Hello,World!" && exit 400

执行脚本

[root@docker test]# sh 20160909-3
Hello,World!
[root@docker test]# echo $?
144

我指定的是400,真实的值为144.为什么会是这个值?

首先退出状态码取值范围为0-255。一般在不指定退出状态码时,所有的状态值都会在0-255之内,当我们手动指定退出状态码后,若值超过255,那么shell会通过模(模就是256)运算得到相应的退出状态码400对256取模值为144.


函数返回值(函数退出状态码)

bash shell会把函数当做小型脚本,运行结束时也会返回一个退出状态码。

默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。

示例:函数状态码演示

#!/bin/bash
#function exit status
func_test() {
  echo "Hi,this is a function test!"
  lll -a /etc
}
func_test
echo "The exit status is: $?"

运行脚本

[root@docker test]# sh 20160909-4
Hi,this is a function test!
20160909-4:行5: lll: 未找到命令
The exit status is: 127

函数的退出状态码是127,这是因为函数中最后一条命令没有成功执行,更具体点就是没有找到lll命令。但这个退出状态码无法知道函数中其他命令是否成功执行。


再看一个例子

#!/bin/bash
#function exit status
func_test() {
  lll -a /etc
  echo "Hi,this is a function test!"
}
func_test
echo "The exit status is: $?"

仅仅是将先前函数体中的2个命令换个位置。执行该脚本

[root@docker test]# sh 20160909-5
20160909-4:行4: lll: 未找到命令
Hi,this is a function test!
The exit status is: 0

这次,由于函数最后一行命令正确执行,函数的退出状态码就是0,尽管函数中有一条命令没有成功运行。

使用函数的默认退出状态码是很危险的,幸运的是return命令可以解决这个问题。

bash shell使用return命令来退出函数并返回特定的退出状态码。return命令允许指定一个整数值来定义函数的退出状态码,整数范围为0-255

示例:使用return指定函数退出状态码

#!/bin/bash
#using the return command in a fuction
func_test() {
  lll -a /etc 
  echo "Hi,this is a function test!"
  return 4
}
func_test
echo "The exit status is: $?"

还是使用相同的函数,在函数最后加上return指定的状态码4.

执行脚本

[root@docker test]# sh 20160909-6
20160909-4:行4: lll: 未找到命令
Hi,this is a function test!
The exit status is: 4

之所以费这么大劲介绍函数状态码是因为在今后的脚本中一个设置规范的函数返回值能帮我们简化脚本



传递参数给函数

在函数体中,可以使用$1,$2,...引用传递给函数的参数;还可以在函数中使用$*$@引用所有参数,$#引用传递的参数个数;在调用函数时,在函数名后以空白符分隔给定参数列表即可。

示例:传递参数给函数

#!/bin/bash
#传递参数给函数
func_var() {
#输出所有的参数
echo "all parameters are $*"
#输出脚本名称
echo "the script‘s name is $0"
#输出第一个参数
echo "the first parameter is $1"
#输出第二个参数
echo "the second parameter is $2"
}
func_var hello world

执行脚本

[root@docker test]# sh 20160909-7
all parameters are hello world
the script‘s name is 20160909-7
the first parameter is hello
the second parameter is world



函数的变量作用域

局部变量:作用域是函数的生命周期;在函数结束时被自动销毁。

定义局部变量的方法:local VAR=VALUE

本地变量:作用域是运行脚本的shell进程的生命周期;因此,其作用范围为当前shell

为什么要用到局部变量?

下面举个例子说明这个问题

#!/bin/bash
#在函数外定义本地变量
var="Hello,World"
func_1() {
#在函数内改变变量内容
var="Hi,var is changed"
}
echo "$var"
func_1
echo "$var"

执行脚本

[root@docker test]# sh 20160909-8
Hello,World
Hi,var is changed

结果显示在调用函数后,原有的本地变量var被替换了。还好这个变量并不是重要的部分,想想若是PATH被替换了,那么这个函数的罪过就大了。因此我们如何即调用函数中定义的变量同时又不对本地变量造成任何影响呢?局部变量的出现就是为了解决这个问题

下面看看在使用了局部变量后的效果。

#!/bin/bash
#在函数外定义本地变量
var="Hello,World"
func_1() {
#在函数内改变变量内容
local var="Hi,var is changed"
echo "$var"
}
echo "$var"
func_1
echo "$var"

运行脚本

[root@docker test]# sh 20160909-9
Hello,World
Hi,var is changed
Hello,World

该实验结果说明,使用局部变量后,函数体中出现的变量作用范围只存在于当前函数生命周期。



递归函数

递归函数:即函数可以直接或间接地调用自身

在函数的递归调用中,函数既是调用者,又是被调用者。

递归函数的调用过程就是反复地调用其自身,每调用一次就进入新的一层。

示例:使用递归函数表示阶乘表达式

#!/bin/bash
#using recursion
func_r() {
if [ $1 -eq 1 -o $1 -eq 0 ];then
  echo "1"
else
  local temp=$[ $1 - 1 ]
  local result=`func_r $temp`
  echo $[ $result * $1 ]
fi
}
read -p "Enter a number: " value
[ -z $value ] && echo "None,please inset a number" && exit 1
result=`func_r $value`
echo "The func_r of $value is :  $result"

执行脚本

[root@docker test]# sh 20160909-11
Give a number : 3
The func_r of 3 is : 6
[root@docker test]# sh 20160909-11
Give a number : 5
The func_r of 5 is : 120

递归函数在新手看来是比较难理解的,如果理解起来比较困难,建议给一个小的数,通过列举,来看该函数的执行效果,一层一层,一直到其结束。这样会对递归有个更清晰的认识。



小结

本文主要介绍内容:

函数、函数的语法格式、函数的调用、函数的链接、给函数传递参数、函数返回值、函数的变量作用域、递归函数。



参考书籍:

shell入门到精通

Linux命令行与shell脚本编程大全(第2版)

本文出自 “张帆-IT的奇幻漂流” 博客,请务必保留此出处http://chawan.blog.51cto.com/9179874/1851236

Linux基础之bash脚本进阶篇-函数