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

简介:

函数,什么是函数?

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

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

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

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



函数的语法格式

    格式1:

    function function_name() {

       ... 函数体

    }

    格式2:

    function_name () {

       ... 函数体

    }



函数的调用

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

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

1
2
3
4
5
6
7
8
9
#!/bin/bash
#function sayhello
#author chawan
#定义函数
function  sayhello () {
echo  "Hello,World!"
}
#调用函数
sayhello

运行脚本

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

函数调用成功。

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

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

下面通过实验来测试:

1
2
3
4
5
6
7
8
9
#!/bin/bash
#function sayhello
#author chawan
#调用函数
sayhello
#定义函数
function  sayhello () {
echo  "Hello,World!"
}

运行脚本

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

系统报错,为什么报错呢

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

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

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



函数的链接

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
#函数间的调用
#author chawan date:20160909
john() {
echo  "Hello,John!"
}
tom() {
john
echo  "Hello,tom!"
}
sayhello() {
tom
lilei
}
lilei() {
echo  "Hello,lilei!"
}
sayhello

运行脚本,结果如下:

1
2
3
4
[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中执行的最后一条命令的退出状态码:

示例:查看命令状态码

1
2
3
4
5
6
7
[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+x Linux信号x的严重错误
130 命令通过Ctrl+C终止
255 退出状态码越界

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

示例:指定退出状态码

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

执行脚本

1
2
3
4
[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会把函数当做小型脚本,运行结束时也会返回一个退出状态码。

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

示例:函数状态码演示

1
2
3
4
5
6
7
8
#!/bin/bash
#function exit status
func_test() {
   echo  "Hi,this is a function test!"
   lll -a  /etc
}
func_test
echo  "The exit status is: $?"

运行脚本

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

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


再看一个例子

1
2
3
4
5
6
7
8
#!/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个命令换个位置。执行该脚本

1
2
3
4
[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指定函数退出状态码

1
2
3
4
5
6
7
8
9
#!/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.

执行脚本

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

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



传递参数给函数

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

示例:传递参数给函数

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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

执行脚本

1
2
3
4
5
[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

为什么要用到局部变量?

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

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
#在函数外定义本地变量
var= "Hello,World"
func_1() {
#在函数内改变变量内容
var= "Hi,var is changed"
}
echo  "$var"
func_1
echo  "$var"

执行脚本

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

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

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

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
#在函数外定义本地变量
var= "Hello,World"
func_1() {
#在函数内改变变量内容
local  var= "Hi,var is changed"
echo  "$var"
}
echo  "$var"
func_1
echo  "$var"

运行脚本

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

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



递归函数

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/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"

执行脚本

1
2
3
4
5
6
[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

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



小结

本文主要介绍内容:

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










本文转自 紫色的茶碗 51CTO博客,原文链接:http://blog.51cto.com/chawan/1851236,如需转载请自行联系原作者
目录
相关文章
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
706 3
Linux系统初始化脚本
|
7月前
|
存储 安全 Unix
七、Linux Shell 与脚本基础
别再一遍遍地敲重复的命令了,把它们写进Shell脚本,就能一键搞定。脚本本质上就是个存着一堆命令的文本文件,但要让它“活”起来,有几个关键点:文件开头最好用#!/usr/bin/env bash来指定解释器,并用chmod +x给它执行权限。执行时也有讲究:./script.sh是在一个新“房间”(子Shell)里跑,不影响你;而source script.sh是在当前“房间”里跑,适合用来加载环境变量和配置文件。
604 9
|
7月前
|
存储 Shell Linux
八、Linux Shell 脚本:变量与字符串
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
614 2
|
9月前
|
Web App开发 缓存 安全
Linux一键清理系统垃圾:释放30GB空间的Shell脚本实战​
这篇博客介绍了一个实用的Linux系统盘清理脚本,主要功能包括: 安全权限检查和旧内核清理,保留当前使用内核 7天以上日志文件清理和系统日志压缩 浏览器缓存(Chrome/Firefox)、APT缓存、临时文件清理 智能清理Snap旧版本和Docker无用数据 提供磁盘空间使用前后对比和大文件查找功能 脚本采用交互式设计确保安全性,适合定期维护开发环境、服务器和个人电脑。文章详细解析了脚本的关键功能代码,并给出了使用建议。完整脚本已开源,用户可根据需求自定义调整清理策略。
1040 1
|
11月前
|
Java Linux
自定义linux脚本用于快速jar包启动、停止、重启
自定义linux脚本用于快速jar包启动、停止、重启
426 29
|
11月前
|
Linux Shell
Centos或Linux编写一键式Shell脚本删除用户、组指导手册
Centos或Linux编写一键式Shell脚本删除用户、组指导手册
320 4
|
11月前
|
Linux Shell 数据安全/隐私保护
Centos或Linux编写一键式Shell脚本创建用户、组、目录分配权限指导手册
Centos或Linux编写一键式Shell脚本创建用户、组、目录分配权限指导手册
567 3
|
监控 安全 Shell
防止员工泄密的措施:在Linux环境下使用Bash脚本实现日志监控
在Linux环境下,为防止员工泄密,本文提出使用Bash脚本进行日志监控。脚本会定期检查系统日志文件,搜索敏感关键词(如"password"、"confidential"、"secret"),并将匹配项记录到临时日志文件。当检测到可疑活动时,脚本通过curl自动将数据POST到公司内部网站进行分析处理,增强信息安全防护。
490 0
|
Linux Shell Windows
4:Bash shell命令-步入Linux的现代方法
4:Bash shell命令-步入Linux的现代方法
287 0