函数介绍
函数function是由若干条shell命令组成的语句块,实现代码 重用和模块化编程 它与shell程序形式上是相似的,不同的是它不是一个单独的 进程,不能独立运行,而是shell程序的一部分
函数和shell程序比较相似,区别在于:
Shell程序在子Shell中运行
而Shell函数在当前Shell中运行。因此在当前Shell中,函 数可以对shell中变量进行修改
定义函数
函数由两部分组成:函数名和函数体
语法一:
f_name (){
...函数体...
}
语法二:
function f_name {
...函数体...
}
语法三: f
unction f_name () {
...函数体...
}
function的帮助信息
root@(6-1)app# help function
function: function name { COMMANDS ; } or name () { COMMANDS ; }
Define shell function.
Create a shell function named NAME. When invoked as a simple command,
NAME runs COMMANDs in the calling shell's context. When NAME is invoked,
the arguments are passed to the function as $1...$n, and the function's
name is in $FUNCNAME.
Exit Status:
Returns success unless NAME is readonly.
函数使用
函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止
函数有两种返回值:
函数的执行结果返回值:
(1) 使用echo等命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误返回
交互式环境下定义和使用函数
示例:
dir() {
> ls -l >
}
定义该函数后,若在$后面键入dir,其显示结果同ls -l的 作用相同
dir
该dir函数将一直保留到用户从系统退出,或执行了如下所 示的unset命令:
unset dir
在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至 shell首次发现它后才能使用
调用函数仅使用其函数名即可
示例:
[root@localhost hanshu]# cat linshi
#!/bin/bash
#fun1
holle() #定义函数holle
{
echo "holle there taday's date is `date +%F`" #函数体
}
echo "now going to the function hello"
holle #脚本自身调用函数
echo "back from the function"
[root@localhost hanshu]# .linshi #调用函数的输出
now going to the function hello
holle there taday's date is 2018-03-21
back from the function
使用函数文件
可以将经常使用的函数存入函数文件,然后将函数文件载 入shell
文件名可任意选取,但最好与相关任务有某种联系。例如 :functions.main
一旦函数文件载入shell,就可以在命令行或脚本中调用 函数。可以使用set命令查看所有定义的函数,其输出列 表包括已经载入shell的所有函数
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
创建函数文件
函数文件示例
cat functions.main
#!/bin/bash
#functions.main
findit() #定义函数 findit
{
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 –print
}
载入函数
函数文件已创建好后,要将它载入shell
定位函数文件并载入shell的格式:
. filename 或 source filename
注意:此即<点> <空格> <文件名> 这里的文件名要带正确路径
示例:
上例中的函数,可使用如下命令:
. functions.main
检查载入函数
使用set命令检查函数是否已载入。set命令将在shell中显示 所有的载入函数
示例:
set
findit=( )
{
if [ $# -lt 1 ]; then
echo "usage :findit file";
return 1
fi
find / -name $1 -print
}
执行shell函数
要执行函数,简单地键入函数名即可
示例:
findit groups
/usr/bin/groups
/usr/local/backups/groups.bak
删除shell函数
现在对函数做一些改动后,需要先删除函数,使其对shell不 可用。使用unset命令完成删除函数
命令格式为:
unset function_name
示例:
unset findit
再键入set命令,函数将不再显示
环境函数 使子进程也可使用
声明:export -f function_name
查看:export -f 或 declare -xf
函数参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔 给定参数列表即可;例如“testfunc arg1 arg2 ...”
在函数体中当中,可使用$1, $2, ...调用这些参数;还 可以使用$@, $*, $#等特殊变量
函数的返回值
return
0为正确结果
1-255为错误
unset取消函数定义,仅影响当前shell及其子Shell。
函数变量
1.本地函数、局部变量(本地变量)
local 变量名。本地函数中对变量的修改,赋值只在当前函数运行期间有效。
在函数中定义局部变量的方法
local NAME=VALUE
#!/bin/bash
func1 () { #定义函数func1
local name=test #定义一个本地函数
echo "func1:name=$name"
local age=18 #定义一个本地函数
echo "func1:age=$age"
echo $$ #查看当前进程
}
#输出
root@(5-0)app# echo $$ #查看脚本执行前进程号
3541
root@(5-0)app# func1
func1:name=test
func1:age=18
3541 #脚本执行显示的进程号
2.全局函数(本地变量)
不加local及declare -i(仅限整数数字) 默认为全局函数,而declare -ig也为全局函数(本地变量)。全局函数对变量的修改及赋值对当前shell均有效。但子Shell不继承
func1 () { #定义函数
name=test
declare -i age=16
echo "func1:name=$name, func1:age=$age"
}
#输出
root@(5-0)app# . lins #载入函数
root@(5-0)app# func1 #调用函数
func1:name=test, func1:age=16 #函数输出结果
root@(5-0)app# echo $name #在当前bash中调用函数定义的变量
test #变量值
root@(5-0)app# bash - #切换子bash中
[root@localhost app]# echo $name #显示变量
#没有值
3.环境函数(环境变量)
declare -xf 环境函数中的变量会对子shell继承 或 export -f
例题:
使用函数编写脚本,实现打印国际象棋棋盘,要求每个格子由8个空格组成。
方法一:
#!/bin/bash
red() { #定义函数red输出显示样式
echo -e "\033[41m \033[0m\c"
}
yel() { ##定义函数yel输出显示样式
echo -e "\033[43m \033[0m\c"
}
redyel() { #定义函数redyel
for ((i=1;i<=4;i++));do #横向进行循环4次
#因为单个格子宽放大四倍,所以高度也要进行放大四倍
for ((j=1;j<=4;j++));do
#进行判断输出顺序
[ "$1" = "-r" ] && { yel;red; } || { red;yel; }
done
echo
done
}
#每个棋盘8x8,定义纵向格子数量
for ((line=1;line<=8;line++));do
#进行奇偶判断,来调用redyel函数
[ $[$line%2] -eq 0 ] && redyel || redyel -r # -r位置化参数
方法二:
#此方法使用for循环,不做详解
#!/bin/bash
lvse (){
echo -e "\e[1;42m \e[0m\c"
}
huangse (){
echo -e "\e[1;43m \e[0m\c"
}
for i in {1..8};do
for x in {1..4};do
for n in {1..8};do
a=`echo $[$[$i+$n]%2]` #奇偶判断
if [ $a -eq 0 ];then
huangse
else
lvse
fi
done
echo
done
done
函数递归:
函数直接或间接调用自身 (棋盘题第一种方法间接调用)
注意递归层数
递归实例:
阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语 一个正整数的阶乘(factorial)是所有小于及等于该数的正整 数的积,并且有0的阶乘为1,自然数n的阶乘写作n!
n!=1×2×3×...×n
阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!
#!/bin/bash
#定义函数fact
# fact() {
#进行判断$!=0或等于1时输出1
if [ $1 -eq 0 -o $1 -eq 1 ];then
echo 1
else
#否则,$1*$1-1,但是¥1-1不知道值,进行函数调用,得出$1-2的值,
#$1-2取不出来值,在进行函数调用,得出$1-3的值;以此类推。
#最终中所有值取出来进行乘法运算,取出乘积,$1的值就取出来了。
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1
编写函数,实现OS的版本判断
编写函数,实现取出当前系统eth0的IP地址
编写函数,实现打印绿色OK和红色FAILED
编写函数,实现判断是否无位置参数,如无参数,提示错误
#!/bin/bash
#定义OS的版本判断函数:version
version () {
ver=$(cat /etc/redhat-release | sed -r "s/.* ([0-9])[.].*/\1/")
echo "当前版本号为 $ver"
}
#定义取出当前系统eth0的IP地址的函数: ipaddr
ipaddr () {
ip=$(ip a s | grep ens33 | sed -n "2p"| cut -d/ -f1| awk '{print $2}')
echo $ip
}
#定义打印绿色OK和红色FAILED的函数:print_color
print_color() {
echo -e "\033[41mFAILED\033[0m"
echo -e "\033[42mOK\033[0m"
}
#定义是否无位置参数,如无参数,提示错误的函数: judge_canshu
judge_canshu() {
if [ $# -eq 0 ];then
echo "请输入至少一个参数"
else
echo "共有 $# 个参数"
fi
1、
在centos5或6上,编写服务脚本/etc/init.d/routed,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/routed, 并显示“OK”
考虑:如果事先已经启动过一次,则显示routed already runing...
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“OK”
考虑:如果事先已然停止过了,则显示"Faild"
(5) 如是restart,则先stop, 再start
考虑:如果本来没有start,则在stop步骤显示"Faild",而在start步骤显示OK.
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“routed is running...”
如果/var/lock/subsys/SCRIPT_N AME文件不存在,则显示“routed is stopped...”
(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理
(8) 当start该脚本后,会在该主机上配置去往1.1.1.0和2.2.2.0网络的路由去往172.18.0.1.
(9) 当stop该脚本后,会删除(8)的两条路由
(10) 当restart时,会先删除路由,再加上.
#!/bin/bash
# chkconfig: 2345 19 80 #应用于那几个模式 ,并设置开关机启动顺序
# description:routed #服务名称
if ! [ -d /var/lock/subsys ];then
mkdir -p /var/lock/subsys &> /dev/null
fi
. /etc/init.d/functions
start () {
if [ -e /var/lock/subsys/routed ];then
action "Staring routed: routed already runing" false
else
touch /var/lock/subsys/routed
route add -net 1.1.1.0/24 gw 172.18.0.1
route add -net 2.2.2.0/24 gw 172.18.0.1
action "Starting routed:" true
fi
}
stop () {
if ! [ -e /var/lock/subsys/routed ];then
action "Shutting down routed:routed not running" false
else
rm -rf /var/lock/subsys/routed
route del -net 1.1.1.0/24 gw 172.18.0.1
route del -net 2.2.2.0/24 gw 172.18.0.1
action "Shutting down routed:" true
fi
}
status () {
if [ -e /var/lock/subsys/routed ];then
echo "routed is running..."
else
echo "routed is stopped..."
fi
}
case $1 in
start)
start;;
stop)
stop;;
restart)
stop
start;;
status)
status;;
*)
echo "Usage: $0 {start|stop|status|restart} "
esac
2、
编写脚本/root/bin/copycmd.sh
(1) 提示用户输入一个可执行命令名称
(2) 获取此命令所依赖到的所有库文件列表
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下; 如:/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下: 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
if ! [ $# -eq 2 ];then
echo "error, must two integer"
exit 0
elif
#判断输入的参数是不是数字
! [[ $1 =~ [0-9]+ ]] || ! [[ $2 =~ [0-9]+ ]];then
echo "error,$1 or $2 not a integer"
elif
#判断两个参数数值是否相等
[ $1 -eq $2 ];then
echo "$1 and $2 are equal"
elif
#两个参数比较大小
[ $1 -lt $2 ];then
a=$2
echo "$a is larger"
else a=$1
echo "$a is larger"
fi
3.
斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: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阶斐波那契数列
#!/bin/bash
shulie () { #定义函数shulie
if [ $1 -eq 1 ];then #判断输入数是否为1,成立时输出0
echo -n "0 "
elif [ $1 -eq 2 ];then #判断输入数是否为2,成立时输出1
echo -n "1 "
elif [ $1 -gt 2 ];then #判断输入数是否大于2,成立时,输出n-1和n-2的和
#当n=3,先进行函数调用,计算出n-1和n-2的结果在进行加法运算,也就是先计算2和1的结果再相加
#当=8时,调用函数计算,7和6的值,7和6没有给与值,又会再调用函数,可计算组成7的5、6和组成6的4、5,
#但是4、5、6的也没有,所以他们也会再次调用函数计算出组成他们各自的n-1和n-2,以此类推,最后都计算出
#来了,才会推出我们所要的n-1和n-2的值,想加就是我们要的n的值
echo -n "$[$(shulie $[$1-1])+$(shulie $[$1-2])] "
fi
}
a=$1
#做循环就可以将从零到n的各数列值分别列出
for i in $(seq 1 $a);do
shulie $i
done
echo
#调用函数shulie ,并赋值第一个位置变量
#shulie $a
4.汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘,利用函数,实现N片盘的汉诺塔的移动步骤
#!/bin/bash
count=0
n_1()
{
let count++
echo "第${count}步:将${2}号圆盘从${1}移动到${3}"
}
n_2()
{
if [ $1 -eq 1 ];then
n_1 $2 1 $4
else
n_2 $[$1-1] $2 $4 $3
n_1 $2 $1 $4
n_2 $[$1-1] $3 $2 $4
fi
}
read -p "please input the number: " num
n_2 $num X Y Z
num=3
n_2 3 X Y Z
n_2 2 X Z Y
n_2 1 X Y Z
n_1 X 1 Z
输出:第1步:将1由X移到Z
n_1 X 2 Y
输出:第2步:将2由X移到Y
n_2 1 Z X Y
n_1 Z 1 Y
输出:第3步:将1由Z移到Y
n_1 X 3 Z
输出:第4步:将3由X移到Z
n_2 2 Y X Z
n_2 1 Y Z X
n_1 Y 1 X
输出:第5步:将1由Y移动X
n_1 Y 2 Z
输出:第6步:将2由Y移到Z
n_2 1 X Y Z
n_1 X 1 Z
输出:第7步:将1由X移到Z