shell脚本语言
shell脚本入门
| shell是什么
Shell是一个命令解释器,它在操作系统的最外层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出屏幕返回给用户
这种对话方式可以是:
- 交互的方式:从键盘输入命令,通过/bin/bash的解释器,可以立即得到shell的回应
- 非交互的方式:脚本
| Shell能做什么
安装操作系统:
CentOS6.X
和CentOS7.X
手动方式安装或克隆方式自动化安装:cobbler
、kickstart
底层都是shell脚本实现优化 SSH:关闭Selinux 优化防火墙,放行 80、443、SSH端口、zabbix监控等服务访问端口
个人需求:加大文件描述符、时间同步、硬件时间、软件时间、YUM源等,都可以写入shell脚本
安装服务 Nginx、Apache Tomcat、PHP、MySQL、Redis、Mongo、docker等例如:PHP5.4和PHP7.1写入shell脚本,实现自动化安装不同版本的服务
代码上线:shell脚本自动化发布自动化回滚
zabbix监控:硬件、软件、进程、端口号、自定义监控都可以通过shell脚本+定时任务完成
日志分析日志统计:命令三剑客+定时任务+shell脚本来实现在ELK集群上的日志展示
业务层面
辅助开发程序:nohup和python的大量的日志处理
| 如何Shell编程
- 重点掌握前面的内容:变量、判断、bash,对它们彻底理解
- 先看懂,读懂shell脚本
- 讲完判断,将前面学过的脚本进行完善
- 自己写简单的脚本,如一些小的项目:生活中:随机点餐、大保健、会员办理,消费、服务 对应价格不同 结账 会员账号 密码 密码丢失
- 有基本适合自己的教材,如:跟老男孩学习Shell编程或者完善的文档
- 不能拿来及用,要搞懂、变成自己的,吸收了后可以解决企业中大部分的shell问题
一个shell脚本
# cat test.sh
#!/bin/bashecho "Hello World!"
| 执行脚本的三种常用的方式
(1)使用bash或者sh通过解释器执行脚本
在子shell中执行命令
sh test.sh
## Hello World!
bash test.sh
## Hello World!
(2)使用路径方式
全路径执行方式或者当前路径,必须给x权限
chmod +x test.sh
/server/scripts/test.sh
## Hello World!
./test.sh
## Hello World!
(3)使用source或者 . 的方式
. test.sh
## Hello World!
source test.sh
## Hello World!
(4)其他shell的执行方式
cat test.sh | bash
## Hello World!
echo ls | bash
## test.sh
bash < test.sh
## Hello World!
shell变量基础
| 什么是变量
shell变量是一种很**“弱”**的变量,默认情况下,一个变量保存一个串,shell不关心这个串是什么含义,所以若要进行数学运算,必须使用一些命令例如let、declare、expr、双括号等
shell变量可分为两类:局部变量和环境变量
- 局部变量只在创建它们的shell中可用
- 环境变量则可以在创建它们的shell及其派生出来的任意子进程中使用
有些变量是用户创建的,其他的则是专用shell变量
- 变量名必须以字母或下划线字符开头,其余的字符可以是字母、数字(0~9)或下划线字符
- 变量名字是大小写敏感的,环境变量推荐设置为字母大写
- 给变量赋值时,等号周围不能有任何空白符,为了给变量赋空值,可以在等号后跟一个换行符
**变量删除:**用set命令可以查看所有的变量,unset var命令可以清除变量var,var相当于没有定义过
**变量只读:**readonly var可以把var变为只读变量,定义之后不能对var进行任何更改
对shell变量的引用方式很多,用这些方式可以方便的获取shell变量的值,变量值的长度,变量的一个字串,变量被部分替换后的值等等
| 变量值的定义
字符串定义
name=Iamlizhenya
name="I am lizhenya"
echo $name
## I am lizhenya
**双引号和单引号的区别:**双引号解析变量,单引号所见即所得不能解析变量,不加引号可以解析变量
数字的定义
age="12 23 432"
echo $age
## 12 23 432
命令的定义
通过``调用命令
date +%F-%H-%M-%S
## 2022-07-07-18-02-34
time=`date +%F-%H-%M-%S`
echo $time
## 2022-07-07-18-02-40
# 时间是固定 每次调用都相同
echo $time
## 2022-07-07-18-02-40
通过$()调用命令
time=$(date +%F-%H-%M-%S)
echo $time
## 2022-07-07-18-02-52
| 变量可以定义变量
ip=`ifconfig eth0|awk 'NR==2{print $2}'`
echo $ip
## 10.0.0.7
dir=${ip}_${time}
echo $dir
## 10.0.0.7_2022-07-07-18-11-34
当shell脚本中出现2条以上相同的命令就将它们写成变量
| 核心位置变量
名称 | 含义 |
---|---|
$# | 传给脚本的参数个数 |
$0 | 脚本本身的名字 |
$1 | 传递给该shell脚本的第一个参数 |
$2 | 传递给该shell脚本的第二个参数 |
$@ | 传给脚本的所有参数的列表 |
$* | 以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个 |
$$ | 脚本运行的当前进程ID号 |
$? | 显示最后命令的退出状态,0表示没有错误,其他表示有错误 |
$0
的使用
cat test.sh
## #!/bin/bashecho $0
sh test.sh
## test.sh
sh /server/scripts/test.sh
## /server/scripts/test.sh
## $0 的使用方法
## 在脚本给予用户提示 如何使用脚本
cat test.sh
## #!/bin/bash
## echo $0
## echo $"Usage: $0 {start|stop|status|restart|force-reload}"
sh test.sh
## test.sh
## Usage: test.sh {start|stop|status|restart|force-reload}
$n
脚本的参数
cat test.sh
## #!/bin/bash
## echo $1
sh test.sh
## oldboyoldboy
## 序列传参
cat test.sh
## #!/bin/bash
## echo $3
sh test.sh {a..z}
## c
sh test.sh {1..10}
## 3
$#
获取脚本传参的总个数
cat test.sh
## #!/bin/bash
## echo $1 $2 $3
## echo $#
sh test.sh 1 2 3
## 1 2 3
## 3
sh test.sh {1..20}
## 1 2 3
## 20
$?
获取上一条命令的返回值
ls
echo $?
## 0
llll
echo $?
## 127
## 案例:
cat ping.sh
## #!/bin/bash
## ping -c1 -W1 $1 &>/dev/null
## [ $? -eq 0 ] && echo "$1 通的" || echo "$1 不通"
sh ping.sh www.baidu.com
## www.baidu.com 通的
sh ping.sh www.baidu.commmmmm
## www.baidu.commmmmm 不通
$$
在有多个相同名称的shell环境中使用
| 脚本传参的三种方式
- 直接传参
cat test.sh
## #!/bin/bash
## echo $1 $2
sh test.sh oldboy 100
## oldboy 100
- 赋值传参
cat test.sh
## #!/bin/bash
## name=$1
## age=$2
## echo $name
## echo $age
sh test.sh oldboy 200
## oldboy
## 200
cat test.sh
## #!/bin/bash
## name=$1
## age=$2
## echo 姓名: $name
## echo 年龄: $age
sh test.sh oldboy 100
## 姓名: oldboy
## 年龄: 100
read
读入
read -p "请输入你的姓名: " name请输入你的姓名: oldboy
echo $name
## oldboy
## 第一种书写方式
cat test.sh
## #!/bin/bash
## read -p "请输入你的姓名: " name
## read -p "请输入你的年龄: " agee
## cho name=$name
## echo age=$age
## 第二种书写方式
cat test.sh
## #!/bin/bash
## read -p "请输入你的姓名和年龄: " name age
## echo name=$name
## echo age=$age
shell变量子串
| 子串的切片
name=1234567
echo ${name:2:2}
## 34
echo ${name:2:3}
## 345
echo ${name:2:4}
## 3456
| 子串的长度统计
name=1234567
## 第一种统计方式:
echo $name|wc -L
## 第二种统计方式:
expr length "$name"
## 第三种统计方式:
echo $name|awk '{print length}'
## 第四种统计方式:
echo ${#name}
| 子串的删除(支持通配符)
# url='www.baidu.com'
## 1.从前面往后删除 (贪婪匹配)
echo ${url#www.}
## baidu.com
echo ${url#*.}
## baidu.com
echo ${url#*.*c}
## om
echo ${url#*.*.}
## com
echo ${url##*.}
## com
## 2.%从后面往前删除
echo ${url%.*}
## www.baidu
echo ${url%.*.*}
## www
echo ${url%%.*}
## www
| 子串的替换
## 格式:/替换谁/替换成谁 /// 贪婪替换
url='www.baidu.com'
echo ${url/w/W}
## Www.baidu.com
echo ${url//w/W}
## WWW.baidu.com
echo ${url/www/WWW}
## WWW.baidu.com
echo ${url/baidu/sina}
## www.sina.com
shell数值运算
| expr 只支持整数运算
expr 1 + 1
## 2
expr 10 - 10
## 0
expr 10 \* 10
## 100
expr 10 / 10
## 1
## 错误运算方式:
expr 1 + 1.5
## expr: non-integer argument
expr 10 * 10
## expr: syntax error
## 案例: 判断传入的参数是否为整数
cat expr.sh
## #!/bin/bash
## read -p "请输入你的年龄: " age
## expr 1 + $age &>/dev/null
## [ $? -ne 0 ] && echo "请输入整数" && exite
## cho $age
$(())
只支持整数运算
| 注意不要和
$()
冲突了 执行的命令和``相同
echo $((10+10))
## 20
echo $((10-10))
## 0
echo $((10*10))
## 100
echo $((10/10))
## 1
## 随机数取余数 RANDOM 0-32767之间的数字
echo $((RANDOM%100+1))
## 82
| $[] 只支持整数运算
echo $[10+10]
## 20
echo $[10-10]
## 0
echo $[10/10]
## 1
echo $[10*10]
## 100
## 平方:
echo $[10**10]
## 10000000000
| let 只支持整数运算
let a=1+1
echo $a
## 2
let a=1*10
echo $a
## 10
i++
let i++ =========== let i=i+1
let i=i+1
echo $i
## 2
let i++
echo $i
## 3
## 直接运算 ++i i++ 相同 都是自增1++a
let ++a
echo $a
## 1
在使用变量的情况下两个是有区别i++
先赋值在运算 | ++i
先运算在赋值
a=1
b=1
let i=a++
let c=++b
echo $i
## 1
echo $c
## 2
| bc 支持整数和小数运算
echo 10+10|bc
## 20
echo 10+10.5|bc
## 20.5
echo 10*10.5|bc
## 105.0
echo 10-10.5|bc
## -.5
echo 10/10.5|bc
## 0
| awk 支持整数和小数运算
awk 'BEGIN{print 10+10}'
## 20
echo 10 20|awk '{print $1+$2}'
## 30
echo 10 20|awk '{print $1*$2}'
## 200
echo 10 20|awk '{print $1/$2}'
## 0.5
echo 10 20|awk '{print $1^$2}'
## 100000000000000000000
条件表达式
| 文件表达式
语法结构:
第一种:
test -f /etc/hosts && echo "文件存在"
## 文件存在
test -f /etc/host && echo "文件存在" || echo "文件不存在"
## 文件不存在
test -f /etc/hosts && echo "文件存在" || echo "文件不存在"
## 文件存在
第二种: 常用
[ -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
## 文件存在
[ -f /etc/passwdddd ] && echo "文件存在" || echo "文件不存在"
## 文件不存在
[ -d /etc/passwd ] && echo "文件夹存在" || echo "文件夹不存在"
## 文件夹不存在
[ -d /etc/ ] && echo "文件夹存在" || echo "文件夹不存在"
## 文件夹存在
[ -x /etc/ ] && echo "可执行文件存在" || echo "可执行文件不存在"
## 可执行文件存在
ll -d /etc
## drwxr-xr-x. 79 root root 8192 Oct 19 09:14 /etc
[ -e /etc/ ] && echo "文件存在" || echo "文件不存在"
## 文件存在
[ -w /etc/hosts ] && echo "可写文件存在" || echo "可写文件不存在"
## 可写文件存在
注意: 表达式中支持变量和命令
dir=/tmp
[ -d $dir ] && echo "文件存在" || echo "文件不存在"
## 文件存在
dir=/tmppppp
[ -d $dir ] && echo "文件存在" || echo "文件不存在"
## 文件不存在
ls -d /etc/sysconfig/
## /etc/sysconfig/
[ -d `ls -d /etc/sysconfig/` ] && echo "文件存在" || echo "文件不存在"
## 文件存在
| shell数值比较
语法结构第一种:test 整数1 比较符 整数2第二种:[ 整数1 比较符 整数2 ]
比较符 | 作用 |
---|---|
-eq | 等于 |
-ge | 大于或者等于 |
-gt | 大于 |
-le | 小于或者等于 |
-lt | 小于 |
-ne | 不等于 |
test 10 -eq 10 && echo 成立 || echo 不成立
## 成立
test 10 -ne 10 && echo 成立 || echo 不成立
## 不成立
[ 10 -eq 10 ] && echo 成立 || echo 不成立
## 成立
[ 10 -ne 10 ] && echo 成立 || echo 不成立
## 不成立
[ 15 -ne 10 ] && echo 成立 || echo 不成立
## 成立
[ 15 -gt 10 ] && echo 成立 || echo 不成立
## 成立
[ 10 -ge 10 ] && echo 成立 || echo 不成立
## 成立
[ 10 -le 10 ] && echo 成立 || echo 不成立
## 成立
## 支持命令
[ 50 -gt `echo $((RANDOM%100))|tee file.txt` ] && echo "成立" || echo 不成立
## 成立
cat file.txt
## 20
流程控制语句
| if判断语法格式
单分支: 一个条件一个结果
if [ 条件表达式 ];then
命令的集合
fi
if [ 条件表达式 ]
then
命令的集合
fi
双分支结构: 一个条件 两个结果
if [ 条件表达式 ]
then
执行的命令
else
否则执行什么命令
fi
多分支: 多个条件 多个结果
if [ 条件表达式 ];then
成立执行的命令
elif [ 条件表达式 ];then
成立执行的命令
elif [ 条件表达式 ];then
成立执行的命令
else
以上条件都没匹配到 执行的命令
fi
案例1:根据不同的操作系统版本 安装不同的YUM源
- 如何查看操作系统的版本cat /etc/redhat-release
- 使用if多分支进行判断
- 执行不同的命令
- 测试
#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
## 更新前进行备份
backup_yum='mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup'
os_version=`cat /etc/redhat-release |awk '{print $(NF-1)}'`
## 判断网络是否正常
ping -c1 -W1 developer.aliyun.com &>/dev/null
if [ $? -ne 0 ];then
echo "网络不正常正在重启网卡请稍等...."
systemctl restart network
ping -c1 -W1 developer.aliyun.com &>/dev/null
[ $? -ne 0 ] && echo "请管理员检查网络 sendmail....."
fi
## 判断wget是否安装
which wget &>/dev/null
if [ $? -ne 0 ];then
echo "正在安装wget 请稍等....."
yum -y install wget &>/dev/null
[ $? -eq 0 ] && echo "wget 安装成功将继续更新YUM源....."
fi
## 根据不同的操作系统版本安装不同的YUM源
if [ ${os_version%%.*} -eq 7 ];then
$backup_yum
echo "正在更新YUM仓库请稍后......."
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo &>/dev/null
if [ $? -eq 0 ];then
action "成功更新阿里云YUM仓库" /bin/true
else
action "更新失败请检查网络" /bin/false
fi
elif [ ${os_version%%.*} -eq 6 ];then
$backup_yum
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-6.repo
elif [ ${os_version%%.*} -eq 8 ];then
$backup_yum
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo
fi
案例2:使用if判断比较两个的数字大小
#!/bin/sh
# 判断num1数字如果是不是纯数字 或者num2不是纯数字 表达式都成立
read -p "请输入两个数字: " num1 num2
if [[ ! $num1 =~ ^[0-9]+$ || ! $num2 =~ ^[0-9]+$ ]];then
echo "请输入整数"
exit
fi
# 比较两数大小
if [ $num1 -gt $num2 ];then
echo "$num1>$num2"
elif [ $num1 -lt $num2 ];then
echo "$num1<$num2"
else
echo "$num1=$num2"
fi
案例3:安装不同的PHP版本
#!/bin/sh
cat<<EOF
1.PHP5.4
2.PHP5.5
3.PHP7.1
4.PHP7.3
EOF
read -p "请选择要安装的版本的编号或者是PHP版本号:[1|PHP5.4] " num
if [ $num = 1 -o $num = "PHP5.4" ];then
echo "yum -y install PHP5.4........"
elif [ $num = 2 -o $num = "PHP5.5" ];then
echo "yum -y install PHP5.5........"
elif [ $num = 4 -o $num = "PHP7.3" ];then
echo "yum -y install PHP7.3........"
fi
| for循环
语法格式:
for 变量 in 值的列表 值: 数字 字符串 命令 序列 默认以空格来分隔
do
执行的动作 命令的集合
done
输出结果可以和变量相关 也可以不相关
## 和变量相关:
# cat for.sh
#!/bin/sh
for num in 1 2 3
do
echo $num
done
## 和变量不相关:
# cat for.sh
#!/bin/sh
for num in 1 2 3
do
echo hehe
done
## 统计for循环总共循环了多少次:
# cat for.sh
#!/bin/sh
for num in {1..1000}
do
let i++
done
echo $i
for循环 值为命令
输出结果可以和变量相关 也可以不相关
# cat for.sh
#!/bin/sh
for num in `cat /etc/hosts`
do
echo $num
done
for循环案例:
ping 一个c的地址 通表示在线 不通表示离线 10.0.0.1-10.0.0.254
# cat for.sh
#!/bin/sh
for i in {1..254}
do
ping -c 1 10.0.0.${i} &>/dev/null
[ $? -eq 0 ] && echo "10.0.0.${i} 服务器在线" || echo "10.0.0.${i} 服务器不在线"
done
for循环案例: 批量创建用户
# !/bin/sh
read -p "请输入要创建用户的个数: " num
read -p "请输入要创建用户的前缀: " prefix
for i in `seq $num`
do
user=${prefix}$i
id $user &>/dev/null
if [ $? -ne 0 ];then
useradd $user &>/dev/null
[ $? -eq 0 ] && echo "$user 用户创建成功"
else
echo "useradd: user $user already exists"
fi
done
for循环添加和删除用户 不带密版
# !/bin/sh
read -p "请输入用户的个数: " num
read -p "请输入用户的前缀: " prefix
echo "当前输入的用户名为:"
for i in `seq $num`
do
echo "${prefix}$i"
done
read -p "请问您是要删除以上用户还是创建以上用户[del|add]: " re
if [ $re = add ];then
for i in `seq $num`
do
user=${prefix}$i
id $user &>/dev/null
if [ $? -ne 0 ];then
useradd $user &>/dev/null
[ $? -eq 0 ] && echo "$user 用户创建成功"
else
echo "useradd: user $user already exists"
fi
done
elif [ $re = del ];then
for i in `seq $num`
do
user=${prefix}$i
id $user &>/dev/null
if [ $? -eq 0 ];then
userdel -r $user &>/dev/null
[ $? -eq 0 ] && echo "$user 用户删除成功"
else
echo "用户不存在"
fi
done
fi
| while循环
语法结构:
while [ 条件表达式 ] 条件表达式成立(为真)则执行 否则不执行
do
命令
done
使用实例:死循环
## 方式一:
#!/bin/sh
while true
do
echo hehe
sleep 1
done
## 方式二:
#!/bin/sh
while [ 10 -gt 5 ]
do
echo hehe
sleep 1
done
## 方式三:
#!/bin/sh
while [ -f /etc/hosts ]
do
echo hehe
sleep 1
done
while读取文件 取值语法结构:
while read line # line变量名称 自定义
do
执行的命令
done < file
使用实例:
## 方式一:
#!/bin/sh
while read line
do
echo $line
done < /etc/hosts
## 方式二:
#!/bin/sh
while read line
do
user=`echo $line|awk '{print $1}'`
useradd $user
pass=`echo $line|awk '{print $2}'`
echo $pass|passwd --stdin $user
done < user.txt
流程控制语句
命令 | 作用 |
---|---|
exit | 退出整个脚本 |
break | 跳出当前的循环 |
continue | 结束剩下的语句继续从头开始 |
read | 交互 |
使用实例:
## 控制语句exit:
#!/bin/sh
while true
do
echo test.......
exit
echo oldboy.......
done
echo hehe...........
## 控制语句break:
#!/bin/sh
while true
do
echo test.......
break
echo oldboy.......
done
echo hehe...........
## 控制语句continue:
#!/bin/sh
while true
do
echo test.......
continue
echo oldboy.......
done
echo hehe...........
流程控制语句案例:
exit 创建完oldboy5用户 退出当前脚本 只能创建5个用户
#!/bin/sh
for i in `seq 5`
do
user=oldboy$i
id $user &>/dev/null
if [ $? -eq 0 ];then
exit
else
useradd $user &>/dev/null
[ $? -eq 0 ] && echo "$user 创建成功"
fi
done
break 创建完oldboy5用户 跳出本层循环继续往下执行 创建5个用户
#!/bin/sh
for i in `seq 5`
do
user=oldboy$i
id $user &>/dev/null
if [ $? -eq 0 ];then
break
else
useradd $user &>/dev/null
[ $? -eq 0 ] && echo "$user 创建成功"
fi
done
echo oldboy
continue 在oldboy1到oldboy5用户已经存在情况下继续向下创建用户
#!/bin/sh
for i in `seq 10`
do
user=oldboy$i
id $user &>/dev/null
if [ $? -eq 0 ];then
continue
else
useradd $user &>/dev/null
[ $? -eq 0 ] && echo "$user 创建成功"
fi
done
echo hehe...................
break等级跳 ,break只会跳出当前层循环
#!/bin/sh
while true
do
echo "第一层"
while true
do
echo 第二层
sleep 1
while true
do
echo 第三层
sleep 1
break 3
echo oldboy.......
done
done
done
echo hehe...........
shell函数
- 函数是命令的集合 完成特定功能的代码块
- 函数代码块 方便复用
- 函数类似变量 只有先定义才能执行
**区别:**变量不调用也会执行 name=oldboy 函数只有调用才会执行代码
| 函数的定义
#!/bin/sh
fun1(){
echo "函数的第一种定义方式"
}
fun1
function fun2 {
echo "函数的第二种定义方式"
}
fun2
案例: 菜单
#!/bin/sh
fun1(){
echo -e "\t\t\t\t1.包子"
echo -e "\t\t\t\t2.麻辣烫"
echo -e "\t\t\t\t3.小米粥"
echo -e "\t\t\t\t4.汉堡"
echo -e "\t\t\t\t5.烧烤"
}
fun1
| 函数复用
# cat 1.sh
#!/bin/bash
. /server/scripts/day4/test.sh
fun3
# sh 1.sh
函数的第三种定义方式
# cat test.sh
#!/bin/sh
fun1(){
echo "函数的第一种定义方式"
}
function fun2 {
echo "函数的第二种定义方式"
}
function fun3(){
echo "函数的第三种定义方式"
}
案例: 函数的传参
函数调用在函数名称的后面
fun1 参数1 参数2 参数3 对应函数中的 $1 $2 $3
函数判断文件是否存在
# cat fun.sh
#!/bin/sh
fun1(){
if [ -f $1 ];then
echo "$1 文件存在"
else
echo "$1 文件不存在"
fi
}
fun1 /etc/hosts
# sh fun.sh
/etc/hosts 文件存在
# cat fun.sh
#!/bin/sh
fun1(){
if [ -f $3 ];then
echo "$3 文件存在"
else
echo "$3 文件不存在"
fi
}
fun1 /etc/hosts /etc/passwd /etc/fsttttttt
# sh fun.sh
/etc/fsttttttt 文件不存在
# cat fun.sh
#!/bin/sh
fun1(){
if [ -f $1 ];then
echo "$1 文件存在"
else
echo "$1 文件不存在"
fi
}
fun1 $2
# sh fun.sh /etc/passwd /etc/hosts
/etc/hosts 文件存在
函数可以识别变量
#!/bin/sh
file=$1
fun1(){
if [ -f $file ];then
echo "$file 文件存在"
else
echo "$file 文件不存在"
fi
}
fun1
# sh fun.sh /etc/hosts
/etc/hosts 文件存在
| 函数变量
可以识别全局变量 函数外的都是当前shell的全局变量
只在函数内生效的变量定义
# cat fun.sh
#!/bin/sh
fun1(){
local num=20
for i in `seq $num`
do
total=$[$count+$i]
done
echo "当前运算的结果是: $total"
}
fun1
echo $num
# sh fun.sh
当前运算的结果是: 20
函数的返回值 return
# cat fun.sh
#!/bin/sh
fun1(){
echo 100
return 50
}
result=`fun1`
echo "当前函数的返回值是: " $?
echo "当前函数的执行结果: " $result
# sh fun.sh
当前函数的返回值是: 50
当前函数的执行结果: 100
利用返回值来判断
错误写法
## 错误写法一:
#!/bin/sh
fun1(){
if [ -f $1 ];then
return 50
else
return 100
fi
}
fun1 $1
[ $? -eq 50 ] && echo "文件存在"
[ $? -eq 100 ] && echo "文件不存在"
## 错误写法二:
#!/bin/sh
fun1(){
if [ -f $1 ];then
return 50
else
return 100
fi
}
fun1 $1
if [ $? -eq 50 ];then
echo 文件存在
elif [ $? -eq 100 ];then
echo 文件不存在
fi
解决方法:
[ $? -eq 50 ] && echo 文件存在 || echo 文件不存在
if [ $? -eq 50 ];then
echo 文件存在
else
echo 文件不存在
fi
## 赋值的方式
re=$?
if [ $re -eq 50 ];then
echo 文件存在
elif [ $re -eq 100 ];then
echo 文件不存在
fi
case语句
语法结构:
case 变量 in 变量 直接传参 赋值传参
匹配模式1)
执行的命令集合
;;
匹配模式2)
执行命令集合
;;
匹配模式3)
执行命令集合
;;
*)
无匹配后序列 执行命令集合
esac
案例
# cat case.sh
#!/bin/sh
case $1 in
Shell)
echo shell......
;;
MySQL)
echo MySQL......
;;
Docker)
echo docker......
;;
*)
echo hehe......
esac
# sh case.sh Shell
shell......
# sh case.sh MySQL
MySQL......
# sh case.sh Docker
docker......
或者的使用
# cat case.sh
#!/bin/sh
case $1 in
Shell|1)
echo shell......
;;
MySQL|2)
echo MySQL......
;;
Docker|hehe)
echo docker......
;;
*)
## 匹配不到可以给用户执行提示
echo "Usage: $0 [Shell|MySQL|Docker]"
esac
# sh case.sh 1
shell.....
# sh case.sh hehe
docker......
# sh case.sh Shell
shell......
案例: 使用case写一个菜单 显示系统的登录 负载 磁盘 内存等信息
- 先写菜单:f查看内存、w查看负载、d查看磁盘、l查看登录信息、m显示菜单
- 让用户输入查看的信息read -p 请输入查看的信息的编号:
- 使用case做判断 执行对应的命令
#!/bin/sh
menu(){
cat<<EOF
1.f查看内存
2.w查看负载
3.d查看磁盘
4.l查看登录信息
5.m显示菜单
6.e退出
EOF
}
menu
while true
do
read -p "请输入你想查看的信息的编号或者字母:[1|f|2|w] " num
case $num in
1|f)
clear
free -h
;;
2|w)
clear
uptime
;;
3|d)
clear
df -h
;;
5|m)
clear
menu
;;
6|e)
exit
;;
*)
echo "Usage: $0 [1|f|2|w]"
esac
done
case案例:Nginx启动脚本
Nginx启动两种方式:1种是systemctl管理启动、1种是命令行直接启动以上两种同时只能使用一种启动
命令行的方式
/usr/sbin/nginx启动
/usr/sbin/nginx -s stop 停止
重启不支持 先停止在启动
/usr/sbin/nginx -s reload查看状态 过滤端口或者PID
#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
nginx='/usr/sbin/nginx'
Te(){
if [ $? -eq 0 ];then
action "Nginx $1 is" /bin/true
else
action "Nginx $1 is" /bin/false
fi
}
case $1 in
start)
$nginx
Te $1
;;
stop)
$nginx -s stop
Te $1
;;
restart)
$nginx -s stop
sleep 1
$nginx
Te $1
;;
reload)
$nginx -s reload
Te $1
;;
status)
Port=`netstat -tnulp|grep nginx|grep master|grep '\btcp\b'|awk '{print $4}'`
echo "Nginx_Port: $Port"
PID=`ps axu|grep nginx|grep master|awk '{print $2}'`
echo "Nginx_PID: $PID"
;;
*)
echo "Usage $0 [start|stop|restart|reload|status]"
esac
case案例: jumpserver 跳板机
- 菜单显示我们可以登录的服务信息:web01 10.0.0.7、web02 10.0.0.8
- 选择登录的服务器
- 使用case匹配
#!/bin/sh
web01=10.0.0.7
web02=10.0.0.8
MySQL=10.0.0.51
BACKUP=10.0.0.41
NFS=10.0.0.31
menu(){
cat<<EOF
1.web01 10.0.0.7
2.web02 10.0.0.8
3.MySQL 10.0.0.51
4.BACKUP 10.0.0.41
5.NFS 10.0.0.31
6.menu 显示菜单
7.exit 退出
EOF
}
menu
trap "" HUP INT TSTP
while true
do
read -p "请输入你要登录服务器的编号或者主机名称:[1|web01|6显示菜单] " num
case $num in
1|web01)
ssh root@$web01
;;
2|web02)
ssh root@$web02
;;
3|MySQL)
ssh root@$MySQL
;;
4|BACKUP)
ssh root@$BACKUP
;;
5|NFS)
ssh root@$NFS
;;
6|menu)
menu
;;
7|exit)
exit
;;
*)
echo "Usage $0 [1|web01|2|web02]"
esac
done
case案例: jumpserver 跳板机扩展
- 运维 开发 权限不同:运维:all权限 所有服务器都可以连接 、开发:只能连接web1 web2
- 设置密码 只能失败三次
- 2级菜单 服务器信息
#!/bin/sh
web01=10.0.0.7
web02=10.0.0.8
MySQL=10.0.0.51
BACKUP=10.0.0.41
NFS=10.0.0.31
users(){
cat<<EFO
1.运维
2.开发
3.退出
EFO
}
users
ops(){
cat<<EOF
1.web01 10.0.0.7
2.web02 10.0.0.8
3.MySQL 10.0.0.51
4.BACKUP 10.0.0.41
5.NFS 10.0.0.31
6.menu 显示菜单
7.exit 退出
EOF
}
dev(){
cat<<EOF
1.web01 10.0.0.7
2.web02 10.0.0.8
3.menu 显示菜单
4.exit 退出
EOF
}
trap "echo 别瞎按小心爆炸" HUP INT TSTP
read -p "请输入你的身份: " au
if [ $au = 1 ];then
>/tmp/yunwei.pwd
while true
do
yunwei=`cat /tmp/yunwei.pwd | wc -l`
if [ $yunwei -lt 3 ];then
read -s -p "请输入运维的密码: " pass
if [ $pass = woshiyunwei ];then
echo "登录成功欢迎牛逼的运维!!!"
break
else
echo "密码不正确请重新输入密码"
echo 1 >> /tmp/yunwei.pwd
continue
fi
else
echo "密码错误次数太多,现已退出"
exit
fi
done
ops
while true
do
read -p "请输入你要登录服务器的编号或者主机名称:[1|web01|6显示菜单] " num
case $num in
1|web01)
ssh root@$web01
;;
2|web02)
ssh root@$web02
;;
3|MySQL)
ssh root@$MySQL
;;
4|BACKUP)
ssh root@$BACKUP
;;
5|NFS)
ssh root@$NFS
;;
6|ops)
ops
;;
7|exit)
exit
;;
*)
echo "Usage $0 [1|web01|2|web02]"
esac
done
elif [ $au = 2 ];then
>/tmp/kaifa.pwd
while true
do
kaifa=`cat /tmp/kaifa.pwd | wc -l`
if [ $kaifa -lt 3 ];then
read -s -p "请输入开发的密码: " pass
if [ $pass = woshikaifa ];then
echo "登录成功欢迎小小的开发!!!"
break
else
echo "密码不正确请重新输入密码"
echo 1 >>/tmp/kaifa.pwd
continue
fi
else
echo "密码错误次数太多,现已退出"
exit
fi
done
dev
while true
do
read -p "请输入你要登录服务器的编号或者主机名称:[1|web01|3显示菜单] " num
case $num in
1|web01)
ssh root@$web01
;;
2|web02)
ssh root@$web02
;;
3|dev)
dev
;;
4|exit)
exit
;;
*)
echo "Usage $0 [1|web01|2|web02]"
esac
done
elif [ $au = 3 ];then
exit
fi
shell变量数组
数组的分类
- 普通数组:只能用数字作为索引
- 关联数组:数字或者字符串作为索引
数组的结构
- 类似变量:一个名称对应一个值,一个筐子里面只装了一个水果(苹果)
- 数组:一个名词对应多个值,一个筐子里面装了很多盒子,每个盒子里有不同的水果
- 索引:盒子的名称,称为索引也称为下标也称为元素名称
数组的格式
数组名称[索引名称]=元素的值筐子[盒子1]=苹果筐子[盒子2]=梨筐子[盒子3]=黄瓜
普通数组的盒子(索引)从0开始筐子[0]=值
| 普通数组的定义方式
**第一种定义方式:**按照索引进行定义
array[1]=shell
array[2]=mysql
array[3]=docker
查看数组:按照索引进行查看
echo ${array[2]}
## mysql
echo ${array[1]}
## shell
echo ${array[3]}
## docker
查看数组中所有的值
echo ${array[*]}
## shell mysql docker
echo ${array[@]}
## shell mysql docker
查看数组中所有的索引(下标) 所有的盒子的名称
echo ${!array[@]}
## 1 2 3
查看索引的总个数
echo ${#array[@]}
## 3
查看系统的普通数组
declare -a
**第二种定义方式:**使用默认的索引来定义
array=(shell mysql docker oldboy)
echo ${array[*]}
## shell mysql docker oldboy
echo ${!array[*]}
## 0 1 2 3
**第三种定义方式:**使用自定义索引和默认索引定义
array=([5]=shell mysql [10]=docker hehe)
echo ${array[*]}
## shell mysql docker hehe
echo ${!array[*]}
## 5 6 10 11
第四种定义方式:
array=(`cat /etc/passwd|awk -F: '{print $1}'`)
echo ${array[*]}
## root bin daemon adm lp sync shutdown halt mail operator games ftp nobody systemd-network dbus polkitd tss abrt sshd postfix test1 test2 test3 test4 test5 zhangsan lisi erdan goudan gousheng baoyi oldboy5 oldboy1 oldboy2 oldboy3 oldboy4 oldboy6 oldboy7 oldboy8 oldboy9 oldboy10 nginx
echo ${!array[*]}
## 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 数组的遍历
unset array
array=(10 20 30 40)
echo ${array[*]}
## 10 20 30 40
for i in ${array[*]}; do
echo $i;
done
## 10203040
案例
array=(10.0.0.1 10.0.0.7 10.0.0.254 www.baidu.com)
for i in ${array[*]};do
ping -c1 -W1 $i;
done
cat array.sh
## #!/bin/sh
## ip=(10.0.0.1www.baidu.com10.0.0.710.0.0.8www.sinaaaa.com10.0.0.254www.weibo.com)
## for i in ${ip[*]}; do
## ping -c1 -W1 $i &>/dev/null
## if [ $? -eq 0 ];then
## echo "$i 在线"
## else
## echo "$i 不在线"
## fi
## done
使用索引的方式进行遍历
echo ${array[*]}
## 10.0.0.1 10.0.0.7
echo ${!array[*]}
## 0 1
echo ${array[0]}
## 10.0.0.1
echo ${array[1]}
## 10.0.0.7
for i in ${!array[*]}; do
echo ${array[$i]};
done
## 10.0.0.110.0.0.7
| 关联数组
使用字符串作为索引,定义方式:
默认的定义方式是普通数组
array[index1]=shell
array[index2]=mysql
array[index3]=docker
echo ${array[*]}
## docker
declare -a |grep array
declare -a array='([0]="docker")'
配置关联数组 提前声明 declare -A 声明关联数组
declare -A array
array[index1]=shell
array[index2]=mysql
array[index3]=redis
echo ${array[*]}
## shell mysql redis
查看索引
echo ${!array[*]}
案例:统计当前的男性和女性出现的次数
cat sex.txtmmfmfx
## for i in `cat sex.txt`; do
## let $i++;
## done
## echo $m3
## echo $f2
## echo $x1
执行过程
# !/bin/sh
declare -A sex
while read line
do
let sex[$line]++
line=m
let sex[m]++
line=x
let sex[x]++
line=f
let sex[f]++
done < sex.txt
直接查看
# !/bin/sh
declare -A sex
while read line
do
let sex[$line]++
done<sex.txt
echo m 出现了 ${sex[m]} 次
echo f 出现了 ${sex[f]} 次
echo x 出现了 ${sex[x]} 次
数组遍历
# !/bin/sh
declare -A sex
while read line
do
let sex[$line]++
done<sex.txt
for i in ${!sex[*]}
do
echo $i 出现了 ${sex[$i]} 次
done
案例: 统计nginx日志每个IP出现的次数
# !/bin/sh
declare -A ip
while read line
do
let ip
[`echo $line|awk '{print $1}'`]++
done</var/log/nginx/access.log
for i in ${!ip[*]}
do
echo $i 出现了 ${ip[$i]} 次
done