介绍
shell 脚本是实现 Linux 系统自动化的重要工具,提高效率,避免重复劳动,例如如下场景
1、系统安装完后的优化、IP地址设置
2、系统安装完后服务的搭建
3、系统资源、服务的监控等
学习 shell 脚本最好有以下基础
1、Linux 中常用的命令
2、常见的服务和搭建、排除、优化,nginx、nfs等等
3、vim/vi 文本编辑器,grep sed awk
内容
1、变量
2、判断语句:if、case
3、循环语句:for、while等
4、函数
5、流程控制语句:continue、break、exit
等等
什么是 shell 脚本
最简单的 shell 脚本就是命令的堆砌 严格的 shell 脚本会包含有命令、判断语句、循环语句、流程控制语句、变量、注释信息等等。
例子
现在要编写一个脚本清除日志,日志为 /access.log
root@test:/home/test# echo 123 > /access.log
root@test:/home/test# cat /access.log
123
root@test:/home/test# vim clean_log.sh
cd /
>/access.log
echo "日志清除完成"
root@test:/home/test# sh clean_log.sh
日志清除完成
root@kunjuee:/home/test# cat /access.log
上面的脚步是不规范的
#!/bin/bash
DIR=/
ROOT_UID=0
#只有root用户才能清除日志
if ["$UID" -ne "$ROOT_UID"]
then
echo "只有root用户才能清除日志"
exit 1
fi
cd $DIR || {
echo "切换目录失败"
exit 2
}
>access.log && echo "清除日志成功"
介绍
#!/bin/bash
第一行一般来说用于指定命令解释器, sh script-name
脚本执行方式:
1. sh scripts-name 或 /bin/bash scripts-name 脚步文件可以没有可执行权限
2. /path/scripts-name 命令全路径,或者相对路径,必须要有可执行权限
3. source /path/scripts-name 或 ./path/scripts-name 能够让脚本中的变量与函数在脚本执行完成后依然生效。
# 查看默认的命令解释器
root@test:~$ echo $SHELL
/bin/bash
在shell脚本中只要以 # 开头,表示注释
一般来说脚本名称会以.sh结尾,但这不是必须的
shell 脚本是按行执行的,并且默认情况下可以没有缩进,但是为了代码的易读性,所以人为的添加缩进
变量
在 shell 中有三种变量:环境变量,自定义全局变量、自定义局部变量
变量名可以由字母(大小写)、数字、下划线等组成,不能以数字开头
环境变量,例如$SHELL,$UID 等,一般可以在 /etc/profile 或 /etc/bashrc等文件中看到
全局变量:export 变量名=value
能够对子 shell 生效
局部变量:变量名=value
定义完后只能当前shell生效
测试
$ aaa=123
$ export bbb=456
$ echo aaa
123
$ echo bbb
456
# 查看当前shell进程号
$ echo $$
12866
# 开启一个子shell
$ bash
$ echo $$
16990
$ echo $aaa
$ echo $bbb
456
$ exit
除了上面的变量外还有一些特殊功能的变量,$0 $? $n $#
$0:脚本名称
$?:上一条命令的执行结果
$n:n就是第几个参数,脚本传参功能
$#:参数的个数
除了以上功能,还有条件测试表达式、文件测试表达式、字符串测试表达式等等,man bash 来进行查看
判断语句 if
if 语句分为单分支语句、双分支语句、多分支语句,并且 if 语句是可以互相嵌套的。
- 单分支语句格式:
if <条件表达式>
then
指令1...
fi
- 双分支语句格式:
if <条件表达式>
then
指令1...
else
指令2...
fi
- 多分支语句格式:
if <条件表达式>
then
指令1...
elif <条件表达式>
then
指令2...
elif <条件表达式>
then
...
else
指令3...
fi
<条件表达式>,这里的条件表达式可以是命令,test [] [[]] (())
测试
现在编写一个脚本,判断某个服务是否运行,如果已经运行则提示服务已经运行并退出,如果没有运行则运行,如果没有安装服务,则先进行安装再运行,在安装前将yum源设置为阿里云的yum源
# grep 是聚合草足,wc -l 是统计数量
$ jps -l | grep file | wc -l
# 查看服务是否已经启动
$ systemctl is-active mysql
# 查看上一条命令是否执行成功
$ echo $?
#
$ ss -lntup
#!/bin/bash
if[$# -eq 1]
then
#判断服务是否安装
#rmp -qa | grep $1 | wc -l
aa=`rpm -qa $1|wc -l`if[$aa -lt 1]
then
#下载阿里云镜像源
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
if [ $? -eq 0]
then
#安装服务
yum install -y $1
systemctl start $1
systemctl enable $1
bb=`systemctl is-active $1`if [ $bb == 'active']
then
echo "${1}服务安装并启动成功"else
echo "${1}服务安装失败,请检查"
fi
else
echo "安装yum源失败,请检查网络"
exit 1
fi
else
cc=`systemctl is-active $1`if [ $cc == "active" ]
then
echo "${1}服务已经开启"else
systemctl start $1if [$? -eq 0 ]
then
echo "${1}服务已经启动成功"else
echo "${1}服务启动失败,检查配置是否正常"
fi
systemctl enable $1
fi
fi
else
echo "请输入要运行的服务,格式为:if.sh 服务名称"
fi
for 循环
for 循环与 while 循环最大的区别在于循环的次数,一般来说 for 循环用于有限次数的循环,while 循环一般来说用于守护进程。
格式
for 变量名 in 变量取值表
do
指令
...
done
例子:
现在有几个 KVM 虚拟机,配置文件是以 xml 结尾的文件,文件中记录着虚拟机的各种信息。现在需要获取虚拟机的名称与磁盘的对应关系,并且以空格分隔。
# 打印 xml 的第九行,| 将前面的结果丢给后面的命令,awk进行列输出,-F 指定分隔符,
$ sed -n 9p centos7-11.xml | awk -F "[<>]" '{print $3}'
#!/bin/bash
file_name=`ls /root/centos7-*.xml`
for n in $file_name
do
kvm_name=`sed -n 9p $n | awk -F "[<>]" '{print $3}' `
disk_name=`sed -n 41p $n | awk -F "'" '{print $2}'`
echo "${n}:${kvm_name} ${disk_name}"
while 循环语句
格式
while 条件表达式(true)一直执行
do
指令
done
例子:
猜随机数,系统随机生成一个1~60之间的数字,用户来猜这个数字,对用户输入的数字进行判断,如果不等于则提示大或小,正确后提示正确,并提示尝试次数。
# 生成随机数 0~32768 之间
$ echo $RANDOM
$ expr 1 + 1
2
$ echo $?
0
$ expr aa + 1
expr:非整数参数
$ echo $?
2
#!/bin/bash
try=0
num=$((RANDOM%61))
one(){
# read 是交互式的
read -p "please input a num 0 ~ 60:" a
# 判断输入的是否为数字
expr $a + 1 & > /dev/nullif [ $? -ne 0 ]
then
echo "请输入正确的内容"
one
fi
}
two(){((try++))
if [ $a -eq $num ]
then
echo "猜对了,数字为:${num}.尝试次数为${try}"
exit 0
elif [ $a -gt $num ]
then
echo "大了,请重试"
one
else
echo "小了,请重试"
one
fi
}
three(){
one
while truedo
two
done
}
three