shell 函数 与 函数库

Posted 我一个月改一次名

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell 函数 与 函数库相关的知识,希望对你有一定的参考价值。

前言

 使用函数是为了实现特定的功能,可以提高代码可读性、易维护、复用性。

 一个函数可以被调用多次,不调用的话,函数中的命令不会执行。

一、shell 函数

  • 将命令序列按格式写在一起;
  • 方便重复使用命令块;
  • 同一脚本文件中,函数体要写在调用函数行的上面;
  • 函数在Shell脚本中仅在当前Shell环境中有效。

1.1 Shell函数定义

格式:

function 函数名() {
}



function 函数名 {
}



函数名 ()
{

}

直接使用函数名访问函数

例:

#!/bin/bash
function fun1() {   #定义一个函数fun1
        echo "这是函数fun1"
}

fun1  #直接使用函数名执行函数

#

result=`fun1  `  #将函数返回结果赋给一个变量,对这个变量进行操作
echo $result

1.2 函数返回值

return 表示退出函数并返回一个退出值,这个值是状态码,不是java中的字符串、集合等变量。脚本中可以用 $? 变量显示该值。

使用原则:

1、函数一结束就取返回值,因为 $? 变量只返回执行的最后一条命令的退出状态码;

2、$? 退出状态码必须是0~255,超出时值将取 除以256取余后的值。

#!/bin/bash
function fun1() {
        num=2
        return $[$num*2]   #retuen 的状态只能用 echo $? 来获取值
}

result=`fun1`   #使用获取变量的方式获取不到return 的值
echo $result    

fun1  #执行函数
echo $?		#获取return 的状态码


result=`back`  #将函数返回结果赋给一个变量,对这个变量进行操作
echo $result

1.3 函数参数传值

 函数的括号中不需要写参数,直接通过 位置参数 和 函数的未知参数 进行传值与获取。

使用函数的位置变量,给函数参数赋值

# $1 获取 函数名后面的第一个参数
# $2 获取 函数名后面的第二个参数
# $3 .....
# .. .....

function sum() {
        echo "$1+$2=$[$1+$2]"     #first 就是 $1 参数、second就是 $2 参数
}

read -p "输入第一个参数:" first
read -p "输入第二个参数:" second
sum $first $second

在这里插入图片描述
注:脚本文件的位置变量 和 函数名的位置变量 同时使用时并不冲突,别搞混了,按顺序来取 $1 $2 $3 …

1.4 函数变量生命周期

函数变量的作用范围:

  • 函数在Shell脚本中仅在当前Shell环境中有效
  • Shell 脚本中变量 默认全局有效
  • 将变量限定在一个函数内部使用 local 命令

shell脚本 全局变量

function fun() {
        i=5
        echo $i
}
i=10   #两个i指向存储空间是一样的,修改一个另一个也会被修改
fun
echo $i

#执行脚本结果
5
5

函数局部 local

function fun() {
        local i=5  #local 局部变量,和外面的i变量存储地址不同,互不影响
        echo $i
}
i=10   #全局变量
fun
echo $i

#执行脚本结果
5
10

1.5 函数中可以调用函数

例1:

servicectl_usage ( ){
	echo "Usage:servicectl <service-name><start|stop|restart|reload|status>"
	return 1
}
chk_centos_ver() {
	grep "Centos.*release 7." /etc/centos-release &> /dev/null && echo "7"
	grep "Centos.*release 6." /etc/centos-release &> /dev/null && echo "6"
	grep "Centos.*release 5." /etc/centos-release &> /dev/null && echo "5"
}

servicectl() {
	[[ -z $1 || -z $2 ]] && servicectl_usage
	[ $(chk_centos_ver)=="7" ] && systemctl $2 ${1}.service || service $1 $2
}
servicectl $1 $2

#service httpd start
#systemctl start httpd

例2:
比如我文件 mytxt.txt 中有内容 param=10 param=20,

写个脚本文件如下内容,执行会输出 A B,但是我把 echo $ss 注释掉之后就不会输出,因为函数A调用函数B,函数B中的echo是把结果返回给函数A,自己并不输出信息到屏幕上,函数A获取后 就能用echo进行输出。

#!/bin/bash
function findvalue() {
        grep "param=10" /root/mytxt.txt &> /dev/null && echo "A"
        grep "param=20" /root/mytxt.txt &> /dev/null && echo "B"
}

function servicectl() {
        ss=`findvalue`
        echo $ss
}

#执行函数
servicectl

二、函数之递归

递归就是在函数A中再次调用函数A(自己调用自己)

使用递归注意点:

  • 一定要有递归的操作(比如加减乘除)
  • 执行递归函数前一定要小心排错,防止容易产生的死循环问题;
  • 递归函数中每个循环选择语句如 if-elif-else 语句块只能有一个 echo 语句,没有条件块的最好用一个echo;
  • 递归函数中的 echo 表示输出结果给上一级递归函数,不是输出到桌面

2.1 阶乘

#!/bin/bash
function Factorial() {
        if [ $1 -eq 1 ];then
                echo 1  
        else
                temp=$[$1-1]
                echo $[$1*$(Factorial $temp)]  
				#当前函数把执行结果 echo 给上一级函数, n*...*3*2*1		
        fi
}

########### main () #############
read -p "请输入阶乘号:" n
total=$(Factorial $n)
echo $total

2.2 遍历目录

小知识:比如我们想从 PATH 路径下 寻找 所有可执行文件,我们可以这样通过 for 遍历去查询

#!/bin/bash

IFS=$IFS:

for folder in $PATH
do
	echo "-----------------$folder"
	f=`find $folder -type f`
	for file in $f
	do
		if [ -x $file ];then
			echo $file   #查找可执行文件
		fi
			
	done
done

我们也可以使用 递归目录 的方式,去查询:

2.2.1 方法一:递归输出目录名和文件名

思路:给函数传入一个 绝对路径的目录名 参数,执行 ls 目录名 查询目录下所有目录和文件;

  • 如果还是目录,则输出这个目录名,然后调用自身递归函数,继续 ls 遍历查询
  • 如果是文件,则输出 文件名。

递归目录 调用递归函数传参时,必须为绝对路径

#!/bin/bash

function listFiles(){
for f in `ls $1`          #ls遍历目录
do
        if [ -d "$1/$f" ];then     #判断是否为目录
                echo "$2$f"        #输出 目录名(第一次$2是空)
                listFiles "$1/$f" " $2"    #再次调用递归函数ls 绝对路径目录名
                							# $2是空格,每一级目录/文件比上一级目录/文件多个空格
        else
                echo "$2$f"     #输出 空格 文件名
        fi
done
}

listFiles "/root/bin" ""   
#listFiles "/root/myshell" ""   多测试几个目录

在这里插入图片描述
可使用 tree 目录名 命令验证结果

2.2.2 方法二:递归输出绝对路径

function listFiles(){
for f in $1/*          #ls遍历目录
do
        if [ -d $f ];then     #判断是否为目录
                echo "$2$f"        #输出 目录名(第一次$2是空)
                listFiles "$f" " $2"   #再次ls 绝对路径目录名, $2是空格,每一集比上一级多个空格
        else
                echo "--$2$f"     #输出 -- 空格 文件名
        fi
done
}

#listFiles"/var/log" ""
listFiles"/root/bin" ""

在这里插入图片描述

2.2.3 递归输出 PATH 路径中的所有可执行文件

#!/bin/bash

function listFiles(){
for f in $1/*          #ls遍历目录
do
        if [ -d $f ];then     #判断是否为目录
                echo "$2$f"        #输出 目录名(第一次$2是空)
                listFiles"$f" " $2"   #再次ls 绝对路径目录名, $2是空格,每一集比上一级多个空格
        else
                echo "--$2$f"     #输出 --空格 文件名
        fi
done
}

##############  main() ###########
IFS=$IFS:

#for folder in `echo $PATH`
for folder in $PATH
do
        echo "-------------------------------$folder"
        listFiles $folder ""
done

sh 脚本名 | more

2.3 斐波那契数列

 斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、…… 以此类推

 斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。


 测试要求:用shell写个脚本文件,输入一个数 n( n>=2 ),输出 Fibonacci 数列的第 n 项,用递归函数实现。

 比如输入 8,应该输出 13 (0 1 1 2 3 5 8 13 21 … )

代码:

#!/bin/bash
str=""
function fiboSeq(){
        if [ $1 -eq 1 ];then   #第一个数为0
                echo 0
        elif [ $1 -eq 2 ];then  #第一个数为1
                echo 1
        else
                i=$[$1-1]
                j=$[$1-2]
                res=$[`fiboSeq $i` + `fiboSeq $j`]   #递归调用进行相加
                echo $res
        fi
}

########## main() ##########
read -p "输入斐波那契第几项:" n
result=`fiboSeq $n`
echo "斐波那契数列的第$n项为:"$result

验证:
在这里插入图片描述


此外还有“兔子繁殖”等问题可以用递归来做

三、函数库

创建函数库:

  • 专门创建一个脚本文件,用作函数库,里面写的全是 一个个函数。
  • 函数库中的函数可以执行特定的功能,可以返回,可以不返回信息,建议最起码得返回一个 return 0

脚本中使用函数库

  • 然后在 执行功能的脚本文件中使用 . 函数库脚本source 函数库脚本, 加载(执行) 该函数库中的所有函数
  • 函数库脚本名必须用绝对路径
  • 不要有无异议的代码

如:
新增函数库文件 funhouse.sh,编辑

#!/bin/bash
#description: 函数库文件

#两个数加法
function add(){
	echo $[$1+$2]
}

function subtract(){
	echo $[$1-$2]
}

function multiply_two(){
	echo $[$1*$2]
}

#三个数乘法
function multiply_three(){
	echo $[$1*$2*$3]
}

function divide(){
	if [ $2 -ne 0 ];then  #除数不能为0
		echo $[$1 / $2]
	else
		echo "除数不能为0"
	fi
}

#阶乘函数
function Factorial(){
	if [ $1 -eq 1 ];then
		echo 1
	else
		#local temp=$[$1-1]
		local temp=$(subtract $1 1)             #用减法函数,并传参
		local result=$(Factorial $temp)
		#echo $[$1*$result]
		echo $(multiply_two $1 $result)		#用乘法函数,并传参
	fi
}

在脚本文件中 txt.sh 编辑

#!/bin/bash
#description: 函数库文件

###main####
. /root/funhouse.sh   #加载函数库文件

#下面就可以直接使用函数库文件中的函数

value1=12
value2=6

jia=`add $value1 $value2`

echo "加法 $value1+$value2="$jia

echo '乘法 2*3*2='`multiply_three 2 3 2`

echo '除法 2/0='`divide 2 0`
echo '除法 12/6='`divide 12 6`

echo '5的阶乘='`Factorial 5`

验证:
在这里插入图片描述

以上是关于shell 函数 与 函数库的主要内容,如果未能解决你的问题,请参考以下文章

Shell中的函数库

shell函数与正则

shell函数与正则

shell学习——关于shell函数库的使用

shell脚本 4 函数与正则

Shell编程之函数