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 函数 与 函数库的主要内容,如果未能解决你的问题,请参考以下文章