函数
在Shell中,函数是一段可重复使用的代码块,用于封装重复使用的代码,以提高代码的简洁性和可读性。
Shell函数的定义有两种格式:
使用function
关键字定义函数:
格式:
function 函数名 {
命令序列
}
或
function 函数名 {
命令序列;
return 返回值;
}
不使用function
关键字定义函数:
格式:
函数名 () {
命令序列
}
或
函数名 () {
命令序列;
return 返回值;
}
函数的返回值
函数的返回值可以通过return
命令显式返回,或者使用echo
等命令输出。
return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值
退出状态码必须是0~255,超出时值将为除以256取余
函数的退出状态码通常用于脚本中判断函数的执行结果。
函数的变量作用范围通常只在脚本内的shell环境中有效
案例1:
#!/bin/bash
function test01 {
read -p "请输入一个整数:" num
return $[num * 2]
}
test01
echo $? # 输出函数test01的返回值
在这个例子中,test01
函数通过read
命令读取用户输入的整数,并使用return
命令返回该整数的两倍。调用该函数后,使用echo $?
输出函数的返回值。
脚本执行效果:
这里因为超出255时值将为除以256取余
400*2=800
800对256求余得32
案例2:
is_server_running(){
systemctl status $1 &>/dev/null
if [ $? -eq 0 ];then
return 0
else
return 1
fi
}
#调用函数,并根据函数返回状态码进行输出
is_server_running $1 && echo "$1 is Running" || echo "$1 is stoped"
函数local 变量
在函数体内执行local 变量,可将变量限定在函数体内部使用,
即local定义的变量值只能在函数体内部使用,出了函数体将无法使用;
案例1:
函数内变量加了local的效果
#!/bin/bash
name () {
echo $name
local name=wang
echo $name
}
name=liu
name
echo $name
加了local的变量只能在函数内使用
函数内变量没加local的效果
#!/bin/bash
name () {
echo $name
name=wang
echo $name
}
name=liu
name
echo $name
没加local的变量在函数外也能使用
函数传参
在Shell脚本中,可以通过在函数定义时指定参数来传递参数,
并在函数内部通过$1, $2, 来分别访问这些参数。
案例1:
#!/bin/bash
function print () {
echo "参数1: $1"
echo "参数2: $2"
}
# 调用函数并传递两个参数
print "Hello" "World"
脚本执行效果:
案例2:
#!/bin/bash
fun () {
echo "Hello $1"
#$1 函数的参数
}
#调用函数,后面跟上参数,这种叫函数传参。
fun $1
#$1 脚本的位置参数,需要在执行脚本时传递。
#注意:
#脚本传递参数,是不会传递给函数的。如何才能做到脚本传参,能够传递到函数中去呢?
#我们可以将脚本的位置参数,作为函数的参数,这样就可以实现联动了。
执行脚本时传递参数
函数库文件
可以将经常使用的函数存入一个单独的文件,
然后将文件载入shell,再进行调用函数。
实现过程:
创建函数文件,只存放函数的定义
在脚本或交互式shell中调用函数库文件
. ./filename
或
source filename
案例1:
函数库文件
vim fun.sh
#!/bin/bash
print1 () {
echo "$1 $2"
}
print2 () {
echo "$1 $2 $3"
}
在脚本中调用函数库文件
#!/bin/bash
. ./fun.sh #调用函数库文件
print1 "你" "好" #调用函数并传参
print2 "你" "也" "好"
脚本执行效果:
实践
案例1:
写一个脚本,实现nginx服务的启动、停止、重启。
#!/bin/bash
ngx_sta () {
if [ $? -eq 0 ];then
echo "nginx is $1 success"
else
echo "nginx is $1 error"
fi
}
case $1 in
start)
systemctl $1 nginx #脚本的位置参数作为函数的参数。
ngx_sta $1
;;
stop)
systemctl $1 nginx
ngx_sta $1
;;
reload)
systemctl $1 nginx
ngx_sta $1
;;
restart)
systemctl $1 nginx
ngx_sta $1
;;
*)
echo "usage: $0 [ start | stop | reload | restart ]"
;;
esac
脚本执行效果:
案例2:
1.写一个类似跳板机的功能
思路梳理:
第一步、免密
前提是连到每台机器之前需要先完成对每台机器的免密工作
先在本地生成密钥对
ssh-keygen
再将生成的公钥发给被登录的机器
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.xx.xxx
按照提示输入被登录的机器的密码即可
第二步、准备脚本
这里用到了一个命令trap
trap命令用于指定在接收到信号后将要采取的动作
trap接收信号之后,可以有三种反应方式:
1,执行一段程序来处理这一信号
2, 接受信号的默认操作
3, 忽视这一信号
第一种形式的trap命令在shell接收到信号时,将执行双引号中的命令。
trap "命令" 信号名或信号编号
为了恢复信号的默认操作,使用第二种形式的trap命令:
trap 信号名或信号编号
第三种形式的trap命令允许忽视信号
trap "" 信号名或信号编号
运行格式:trap command signal
command部分是接收到指定信号时将要采取的行动,
signal部分是要处理的信号名。
以下是一些信号(括号里面的数字是信号的编号)
信号 说明
HUP(1) 挂起,通常因终端掉线或用户退出而引发
INT(2) 中断,通常因按下Crtl+C组合健而引发
QUIT(3) 退出,通常因某些严重的执行错误而引发
ABRT(6) 中止,通常因某些严重的执行错误而引发
ALRM(14) 报警,通常用来处理超时
TERM(15) 终止,通常在系统关机时发送
TSTP(20) 停止进程的运行
例:
trap "" 2
:屏蔽INT信号,按Crtl+C健,终端无反应
trap "" HUP INT TSTP
屏蔽忽视HUP、INT、TSTP信号,挂起、中断、停止进程的运行等操作,程序是不会退出的
脚本内容:
#!/bin/bash
access () { #定义一个函数,输出菜单
cat <<EOF
-------欢迎访问Jumpserver--------------
1) web1-192.168.xx.164
2) web1-192.168.xx.165
3) web1-192.168.xx.167
h) menu
q) exit
---------------------------------------
EOF
}
access #脚本一执行就调用函数
trap "" HUP INT TSTP #屏蔽这些信号,接收到这些信号后终端不会有任何反应
while true #死循环
do
read -p "选择你的操作: [ 1 | 2 | 3 | h | q ] " Action
case $Action in
1)
ssh root@192.168.xx.164 #当输入1 时就会登录192.168.xx.164这台机器
;;
2)
ssh root@192.168.xx.165 #当输入2 时就会登录192.168.xx.165这台机器
;;
3)
ssh root@192.168.xx.167 #当输入3 时就会登录192.168.xx.167这台机器
;;
h)
clear #当输入h就会清理屏幕,再次调用函数输出菜单
access
;;
q)
exit #当输入q 就会退出脚本
;;
*)
continue #输入其他任何,就会结束本次循环,重新提示用户输入,继续下一次的循环
esac
done
脚本执行效果:
主菜单样式:
当输入1 时就会登录192.168.xx.164这台机器
当输入2 时就会登录192.168.xx.165这台机器
当输入3 时就会登录192.168.xx.167这台机器
当输入q 就会退出脚本
输入其他任何,就会结束本次循环,继续下一次的循环,重新提示用户输入
2.实现一个多级菜单
一级菜单可以退出脚本
二级菜单可以退回到一级菜单
具体如下
脚本:
#!/bin/bash
menu_1 () {
cat <<EOF
------------------一级菜单--------------
1) Install Nginx Server
2) Install PHP Server
3) Install Redis Server
4) Quit
----------------------------------------
EOF
}
menu2_nginx () {
cat <<EOF
------------------二级页面--------------
1) Install Nginx 1.23.0
2) Install Nginx 1.24.0
3) Install Nginx 1.25.4
4) 返回上一级菜单
5) Quit
----------------------------------------
EOF
}
menu2_php () {
cat <<EOF
------------------二级页面--------------
1) Install php 8.3.0
2) Install php 8.2.0
3) Install php 7.4.33
4) 返回上一级菜单
5) Quit
----------------------------------------
EOF
}
menu2_redis () {
cat <<EOF
------------------二级页面--------------
1) Install redis 6.0.0
2) Install redis 7.0.0
3) Install redis 7.2.4
4) 返回上一级菜单
5) Quit
----------------------------------------
EOF
}
menu_1 #执行脚本后调用展示一级页面的函数
while true #死循环
do
read -p "输入你需要安装的服务编号: [ 1 | 2 | 3 | 4 ] " action
case $action in
1) #当用户输入1时,展示二级nginx安装页面
clear
menu2_nginx #调用函数,展示二级nginx安装页面
while true
do
read -p "输入你需要安装的服务编号: [ 1 | 2 | 3 | 4 | 5 ] " action_nginx
case $action_nginx in
1)
echo "install nginx 1.23.0 is done"
;;
2)
echo "install nginx 1.24.0 is done"
;;
3)
echo "install nginx 1.25.4 is done"
;;
4) #返回一级页面
clear
menu_1 #调用函数,展示一级页面
break #退出当前循环
;;
5)
exit #退出脚本
echo "bye ~"
;;
*)
continue #结束本次循环,继续执行下一次循环
;;
esac
done
;;
2)
clear
menu2_php
while true
do
read -p "输入你需要安装的服务编号: [ 1 | 2 | 3 | 4 | 5 ] " action_php
case $action_php in
1)
echo "install php 8.3.0 is done"
;;
2)
echo "install php 8.2.0 is done"
;;
3)
echo "install php 7.4.33 is done"
;;
4)
clear
menu_1
break
;;
5)
exit
echo "bye ~"
;;
*)
continue
;;
esac
done
;;
3)
clear
menu2_redis
while true
do
read -p "输入你需要安装的服务编号: [ 1 | 2 | 3 | 4 | 5 ] " action_redis
case $action_redis in
1)
echo "install redis 6.0.0 is done"
;;
2)
echo "install redis 7.0.0 is done"
;;
3)
echo "install redis 7.2.4 is done"
;;
4)
clear
menu_1
break
;;
5)
exit
echo "bye ~"
;;
*)
continue
;;
esac
done
;;
4)
exit
echo "bye ~"
;;
*)
continue
;;
esac
done
脚本执行结果
执行脚本展示,一级菜单页面
按下1进入第一个二级页面
按下数字执行对应安装操作
按下4会清空屏幕,展示一级页面,这里为了展示,又将终端历史输出调了出来
按下5会退出脚本
后面的php和redis操作一样,就不展示了