前言
其实shell的函数很简单 对于学过其他编程语言的同学来说 就是轻而易举
什么是函数
-
shell中允许将一组命令集合或语句形成一段可用代码,这些代码块称为shell函数
-
给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能
-
在shell种定义函数可以使用代码模块化,便于复用代码
-
函数必须先定义才可以使用
如何定义函数
方法一
函数名()
{
函数体(一堆命令的集合,来实现某个功能)
}
方法二
function 函数名()
{
函数体(一堆命令的集合,来实现某个功能)
}
熟悉函数写法
- 阶乘
#!/bin/bash
factorial() {
factorial=1
for((i=1;i<=5;i++))
do
factorial=$[ $factorial * $i ]
echo "$factorial"
done
echo "5的阶乘是:$factorial"
}
factorial
#!/bin/bash
function factorial {
a=1
for ((i=1;i<=10;i++))
do
a=$[ $a * $i ]
echo "$a"
done
echo "10的阶乘是:$a"
}
factorial
- 传参
#!/bin/bash
factorial() {
factorial=1
for((i=1;i<=$1;i++))
do
factorial=$[ $factorial * $i ]
echo "$factorial"
done
echo "$1 的阶乘是:$factorial"
}
factorial $1
factorial $2
factorial $3
# 执行函数
bash factorial.sh 10 8 5
10 的阶乘是:3628800
8 的阶乘是:40320
5 的阶乘是:120
#!/bin/bash
factorial() {
a=1
for i in `seq $1`
do
let a*=$i
echo "$a"
done
echo "$1 的阶乘是:$a"
}
factorial $1
# 执行函数
[root@maomao ~]# bash cheng.sh 10
1
2
6
24
120
720
5040
40320
362880
3628800
10 的阶乘是:3628800
#!/bin/bash
fun () {
read -p "请输入一个数字计算他的阶乘:" num
fun=1
for((i=1;i<=$num;i++))
do
fun=$[ $fun * $i ]
echo "$fun"
done
}
fun $num
函数返回值
- 函数返回值是函数最后一条命令
- shell返回值最高是255
#!/bin/bash
re() {
read -p "enter num:" num
return $[2*$num]
}
re
echo "re return value:$?"
#!/bin/bash
fun() {
read -p "enter num:" num
echo $[2*$num]
}
result=`fun`
echo "fun return value:$result"
#这个返回值不是真的返回值
函数中return说明:
- return可以结束一个函数。类似于循环控制语句break(结束当前循环,执行循环体后面的代码)。
- return默认返回函数中最后一个命令状态值,也可以给定参数值,范围是0-256之间。
- 如果没有return命令,函数将返回最后一个指令的退出状态值。
return可以结束一个函数
#!/bin/bash
fun(){
echo 'maomao'
echo 'zhuzhu'
return
echo 'niuniu'
}
fun
# 执行脚本
[root@maomao ~]# bash test.sh
maomao
zhuzhu
return默认返回函数中最后一个命令状态值
#!/bin/bash
fun(){
echo 'zhuzhu'
echo 'maomao'
return 88
echo 'niuniu'
}
fun
# 执行脚本
[root@maomao ~]# bash test1.sh
zhuzhu
maomao
[root@maomao ~]# echo $?
88
函数传参 位置参数
#!/bin/bash
if [ $# -ne 3 ];then
echo "useage:`basename $0` par1 par2 par3"
exit
fi
fun3() {
echo "$(($1 * $2 * $3 ))"
}
result=`fun3 $1 $2 $3` 这个$1 $2 $3是外面的参数赋给函数里的$1 $2 $3
echo "$result" 你传给程序 程序传给函数
# 执行脚本
[root@maomao ~]# bash parameter.sh 1 2 3
6
[root@maomao ~]# bash parameter.sh 2 3 4
24
如果result=`fun3 $3 $3 $3` 三个$3相乘 3*3*3
[root@maomao ~]# bash parameter.sh 1 2 3
27
函数传参 数组变量
计算数组里的值之乘
#!/bin/bash
num1=(1 2 3 4 5)
num2=(2 4 6 8 10)
array() {
local array=1
for i in $*
do
array=$[ array * $i ]
done
echo "$array"
}
array ${num1[*]}
array ${num2[@]}
# 执行脚本
[root@maomao ~]# bash array.sh
120
3840
数组传参 变成一个新的数组
#!/bin/bash
num=(1 2 3 4 5)
num2=(2 4 6 8 10)
array() {
local newarray=($*)
local i
for((i=0;i<$#;i++))
do
outarray[$i]=$(( ${newarray[$i]} * 5 ))
done
echo "${outarray[*]}"
}
result=`array ${num[*]}`
echo ${result[*]}
result=`array ${num2[@]}`
echo ${result[@]}
# 执行脚本
[root@maomao ~]# bash array1.sh
5 10 15 20 25
10 20 30 40 50
将原数组的值改变
#!/bin/bash
num=(2 4 6 8 10)
array() {
local newarray=()
local i
for i
do
newarray[i++]=$[ $i * 10 ]
done
echo "${newarray[*]}"
}
result=`array ${num[*]}`
echo "${result[*]}"
# 执行脚本
[root@maomao ~]# bash array2.sh
20 40 60 80 100
- 函数接受位置参数 $1 $2 $3 … $n
- 函数接受数组变量 $@ $*
- 函数将接收到所有参数赋值给数组 newarry=($*)
应用案例
- 写一个脚本收集用户输入的基本信息(姓名,性别,年龄),如不输入一直提示输入
- 最后根据用户的信息输出相对应的内容
#!/bin/bash
#该函数实现用户如果不输入内容则一直循环直到用户输入为止,并且将用户输入的内容打印出来
input_fun()
{
input_var=""
output_var=$1
while [ -z $input_var ]
do
read -p "$output_var" input_var
done
echo $input_var
}
input_fun 请输入你的姓名:
或者
#!/bin/bash
fun()
{
read -p "$1" var
if [ -z $var ];then
fun $1
else
echo $var
fi
}
#调用函数并且获取用户的姓名、性别、年龄分别赋值给name、sex、age变量
name=$(input_fun 请输入你的姓名:)
sex=$(input_fun 请输入你的性别:)
age=$(input_fun 请输入你的年龄:)
#根据用户输入的性别进行匹配判断
case $sex in
man)
if [ $age -gt 18 -a $age -le 35 ];then
echo "中年大叔你油腻了吗?加油"
elif [ $age -gt 35 ];then
echo "保温杯里泡枸杞"
else
echo "年轻有为。。。"
fi
;;
woman)
xxx
;;
*)
xxx
;;
esac
综合案例
任务背景
现有的跳板机虽然实现了统一入口来访问生产服务器,yunwei用户权限太大可以操作跳板机上的所有目录文件,存在数据被误删的安全隐患,所以希望你做一些安全策略来保证跳板机的正常使用。
具体要求
- 只允许yunwei用户通过跳板机远程连接后台的应用服务器做一些维护操作
- 公司运维人员远程通过yunwei用户连接跳板机时,跳出以下菜单供选择:
欢迎使用Jumper-server,请选择你要操作的主机:
3. DB1-Master
4. DB2-Slave
5. Web1
6. Web2
h. help
q. exit
- 当用户选择相应主机后,直接免密码登录成功
- 如果用户不输入一直提示用户输入,直到用户选择退出
综合分析
- 将脚本放到yunwei用户家目录里的.bashrc文件里(/shell05/jumper-server.sh)
- 将菜单定义为一个函数[打印菜单],方便后面调用
- 用case语句来实现用户的选择【交互式定义变量】
- 当用户选择了某一台服务器后,进一步询问用户需要做的事情 case…esac 交互式定义变量
- 使用循环来实现用户不选择一直让其选择
- 限制用户退出后直接关闭终端 exit
#!/bin/bash
# jumper-server
# 定义菜单打印功能的函数
menu()
{
cat <<-EOF
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
h. help
q. exit
EOF
}
# 屏蔽以下信号
trap '' 1 2 3 19
# 调用函数来打印菜单
menu
#循环等待用户选择
while true
do
# 菜单选择,case...esac语句
read -p "请选择你要访问的主机:" host
case $host in
1)
ssh root@10.1.1.1
;;
2)
ssh root@10.1.1.2
;;
3)
ssh root@10.1.1.3
;;
h)
clear;menu
;;
q)
exit
;;
esac
done
将脚本放到yunwei用户家目录里的.bashrc里执行:
bash ~/jumper-server.sh
exit
进一步完善需求
为了进一步增强跳板机的安全性,工作人员通过跳板机访问生产环境,但是不能在跳板机上停留。
#!/bin/bash
#公钥推送成功
trap '' 1 2 3 19
#打印菜单用户选择
menu(){
cat <<-EOF
欢迎使用Jumper-server,请选择你要操作的主机:
1. DB1-Master
2. DB2-Slave
3. Web1
4. Web2
h. help
q. exit
EOF
}
#调用函数来打印菜单
menu
while true
do
read -p "请输入你要选择的主机[h for help]:" host
#通过case语句来匹配用户所输入的主机
case $host in
1|DB1)
ssh root@10.1.1.1
;;
2|DB2)
ssh root@10.1.1.2
;;
3|web1)
ssh root@10.1.1.250
;;
h|help)
clear;menu
;;
q|quit)
exit
;;
esac
done
自己完善功能:
1. 用户选择主机后,需要事先推送公钥;如何判断公钥是否已推
2. 比如选择web1时,再次提示需要做的操作,比如:
clean log
重启服务
kill某个进程
补充
1) SIGHUP 重新加载配置
2) SIGINT 键盘中断^C
3) SIGQUIT 键盘退出
9) SIGKILL 强制终止
15) SIGTERM 终止(正常结束),缺省信号
18) SIGCONT 继续
19) SIGSTOP 停止
20) SIGTSTP 暂停^Z