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,如需转载请自行联系原作者
目录
相关文章
|
16天前
|
Ubuntu Linux Shell
Linux 系统中的代码类型或脚本类型内容
在 Linux 系统中,代码类型多样,包括 Shell 脚本、配置文件、网络配置、命令行工具和 Cron 定时任务。这些代码类型广泛应用于系统管理、自动化操作、网络配置和定期任务,掌握它们能显著提高系统管理和开发的效率。
|
20天前
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
35 1
|
30天前
|
Devops 关系型数据库 大数据
1000个开源免费的bash脚本合集
【10月更文挑战第4天】
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
2月前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
453 2
|
1月前
|
存储 Shell Linux
【Linux】shell基础,shell脚本
Shell脚本是Linux系统管理和自动化任务的重要工具,掌握其基础及进阶用法能显著提升工作效率。从简单的命令序列到复杂的逻辑控制和功能封装,Shell脚本展现了强大的灵活性和实用性。不断实践和探索,将使您更加熟练地运用Shell脚本解决各种实际问题
25 0
|
2月前
|
Shell Linux 开发工具
linux shell 脚本调试技巧
【9月更文挑战第3天】在Linux中调试shell脚本可采用多种技巧:使用`-x`选项显示每行命令及变量扩展情况;通过`read`或`trap`设置断点;利用`echo`检查变量值,`set`显示所有变量;检查退出状态码 `$?` 进行错误处理;使用`bashdb`等调试工具实现更复杂调试功能。
|
3月前
|
Linux
Linux0.11 文件打开open函数(五)
Linux0.11 文件打开open函数(五)
46 0
|
6月前
|
Linux 开发者
Linux文件编程(open read write close函数)
通过这些函数,开发者可以在Linux环境下进行文件的读取、写入和管理。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
170 4