shell脚本
一、什么是shell脚本
将需要执行的命令保存到文本中,按照顺序执行,它是解释型的,意味着不需要编译
若干命令+脚本的基本格式+脚本特定语法+思想=shell脚本
二、脚本用途
重复化复杂化的工作,通过把工作的命令写成脚本,以后仅仅需要执行脚本就能完成这些工作。
①自动化软件部署 LAMP/LNMP/Tomcat……
ansible
②自动化管理 系统初始化脚本,批量更改主机密码,推送公钥
初始环境一致,防火墙,yum源 修改主机名……
③自动化分析处理 统计网站访问量
日志统计 ip 访问量 阿帕奇 ngx
④自动化备份 数据库备份、日志转储……
⑤自动化监控脚本 CPU使用率 硬盘大小……调用脚本预处理
三、如何学习shell脚本
1.记忆更多的命令(记忆命令使用功能和场景)
2.掌握脚本的标准格式(指定魔法字节、使用标准的执行方式运行脚本)
3.必须熟悉掌握脚本的基本语法(重点呢)
4.多看并理解–>多模仿(多练)–>多思考(多写)
shell脚本的基本写法
脚本第一行
魔法字符#!指定解释器【必写】
#!/bin/bash 表示以下内容使用bash解释器解析
如果直接将解释器路径写死在脚本里,可能在某些系统就会存在找不到解释器的兼容性问题,所以可以使用:#!/bin/env 解释器
脚本第二部分
注释#说明 对脚本的基本信息进行描述
#!/bin/env bash
#以下是对脚本内容的基本信息的描述
#Name:名字
#Desc:描述 describe
#Path:存放路径
#Usage:用法
#Update:更新时间
#下面是脚本具体内容
#commands
……
脚本第三部分
脚本要实现的具体代码内容
脚本的执行方式
标准执行方式
#赋予可执行权限 chmod +x first_shell.sh
[root@master shell01]# ll
总用量 4
-rwxr-xr-x 1 root root 239 8月 17 01:29 first_shell.sh
[root@master shell01]# pwd
/shell01
[root@master shell01]# /shell01/first_shell.sh
hello word
hello word
hello word
[root@master shell01]# ./first_shell.sh
hello word
hello word
hello word
非标准执行方式
[root@master shell01]# bash first_shell.sh
hello word
hello word
hello word
[root@master shell01]# sh first_shell.sh
hello word
hello word
hello word
[root@master shell01]# dash first_shell.sh
hello word
hello word
hello word
[root@master shell01]# bash -x first_shell.sh
- echo ‘hello word’
hello word - echo ‘hello word’
hello word - echo ‘hello word’
hello word
bash -x 一般用于排错,查看脚本执行过程
bash -n 看脚本语法是否有问题
[root@master shell01]# source first_shell.sh
hello word
hello word
hello word
四、变量
什么是变量
变量是用来临时保存数据的,该数据是可以变化的数据
什么时候需要变量
如果某个内容需要多次使用,并且在代码中重复出现,那么可以用变量代表该内容。这样在修改内容的时候,仅仅需要修改变量的值。
在代码运作的过程中,肯会把某些命令的执行结果保存起来,后续代码需要使用这些结果,就可以直接使用这个变量
变量如何定义
变量名=变量值
[root@master ~]# A=hello 定义变量A
[root@master ~]# echo
A
调用变量
A
,要给钱,不是人民币而是美元“
A 调用变量A,要给钱,不是人民币而是美元“
A调用变量A,要给钱,不是人民币而是美元“”hello
[root@master ~]# echo ${A} 不管姿势多优雅都要给钱
hello
[root@master ~]# A=world 因为是变量所以可以变
[root@master ~]# echo $A 不管你是谁,调用就要给钱
world
[root@master ~]# unset A 取消变量,不玩了
[root@master ~]# echo $A
[root@master ~]#
变量定义规则
①严格区分大小写
②变量名不能包含特殊符号(*?#@)
③变量名不能以数字开头
④等号两边不能有任何空格
⑤变量名尽量做到见名知意
变量定义方式
调用变量中的某一部分
A=123456
echo $A
echo ${A:2:3}
345
可以把一个命令的结果赋值给变量
[root@localhost ~]# A=hostname
[root@localhost ~]# echo KaTeX parse error: Expected 'EOF', got '#' at position 43: …ot@localhost ~]#̲ unset A [root@…(uname -r)
[root@localhost ~]# echo B
B
[root@localhost ~]# echo $B
4.18.0-305.10.2.el8_4.x86_64
交互式定义变量
目的:让用户自己给变量赋值,比较灵活
语法:read [选项] 变量名
选项 释意
-p 定义提示用户信息
-n 定义字符数(限制变量值的长度)
-s 不显示(不显示用户输入的内容)
-t 定义超时时间,默认单位为秒(限制用户输入变量值时间)
- 用户自定义变量
[root@localhost ~]# read name
gzh
[root@localhost ~]# echo $name
gzh
[root@localhost ~]#
[root@localhost ~]# read -p “Input your name:” name
Input your name:LYY
[root@localhost ~]# echo $name
LYY
[root@localhost ~]#
[root@localhost ~]# read -s -p “Input your password:” pass
Input your password:[root@localhost ~]#
[root@localhost ~]# echo $pass
456
[root@localhost ~]# read -t 10 -n 5 -p “Input your name:” name
Input your name:12[root@localhost ~]#
[root@localhost ~]# echo $name
12
[root@localhost ~]#
-
变量值来自文件
[root@localhost ~]# mkdir shell01
[root@localhost ~]# chmod +x shell01/
[root@localhost ~]#
[root@localhost ~]# cd shell01/
[root@localhost shell01]# vim ip.txt
[root@localhost shell01]# cat ip.txt
10.1.1.1
[root@localhost shell01]# read -p “请输入IP地址:” IP < ip.txt
[root@localhost shell01]# echo $IP
10.1.1.1
定义有类型的变量(delare)
目的:给变量做一些限制,固定变量的类型,比如:整型,只读
用法:declare 选项 变量名=变量值
选项 释意 举例
-i 将变量看成整数(整型) delcare -i A=123
-r 定义只读变量 delacre -r B=hello
-a 定义普通数组;查看普通数组
-A 定义关联数组;查看关联数组
-x 将变量通过环境导出 delacre -x AAA=123456 等于 export AAA=123456
root@localhost shell01]# A=123
[root@localhost shell01]# echo $A
123
[root@localhost shell01]# A=hello
[root@localhost shell01]# echo $A
hello
[root@localhost shell01]# declare -i A=123
[root@localhost shell01]# echo $A
123
[root@localhost shell01]# A=hello
[root@localhost shell01]# echo $A
0
[root@localhost shell01]# A=333
[root@localhost shell01]# echo $A
333
[root@localhost shell01]# declare -r B=hello
[root@localhost shell01]# echo $B
hello
[root@localhost shell01]# B=888
-bash: B: 只读变量
[root@localhost shell01]# unset B
-bash: unset: B: 无法取消设定: 只读 variable
[root@localhost shell01]# exit 退出程序 只读变量是临时的
[root@localhost shell01]# AAAA=gzh
[root@localhost shell01]# env|grep AAAA
[root@localhost shell01]# export AAAA
[root@localhost shell01]# env|grep AAAA
AAAA=gzh
[root@localhost shell01]# declare -x BBBB=itcast
[root@localhost shell01]# env|grep BBB
BBBB=itcast
[root@localhost shell01]#
变量分类
本地变量:当前用户自定义的变量。当前进程中有效,
环境变量:当前进程有效,并且能够被子进程调用。
- env 查看当前用户的环境变量
- set 查询当前用户的所有变量(临时变量与环境变量)
- export 变量名=变量值 或者 变量名=变量值;export 变量名
全局变量
全局所有的用户和程序都能调用,且继承,新建用户也默认能调用
文件名 说明 备注
$HOME/.bashrc 当前用户的bash信息 定义别名、umask、函数等
用户登录时读取
$HOME/.bash_profile 当前用户的环境变量
用户登录时读取
$HOME/.bash_logout 当前用户退出当前 定义用户退出时执行的程序等
shell时最后 读取
/etc/bashrc 全局的bash信息,
所有用户都生效
/etc/profile 全局环境变量信息 系统和所有用户都生效
系统变量(内置bash中变量):shell本身已经固定好了它的名字和作用
内置变量 | 含义 |
---|---|
$? | 上一条命令执行后返回的状态;状态值为0表示执行正常,非零表示执行异常或错误 |
$0 | 当前执行的程序或者脚本名 |
$# | 脚本后面接的参数的个数 |
$* | 脚本后面所有参数,参数当成一个整体输出,每个变量参数之间以空格隔开 |
$@ | 脚本后面所有的参数,参数是独立的,也是全部输出 |
$1~$9 | 脚本后面的位置参数,$1表示第一个位置参数,依次类推 |
10 {10}~ 10 {n} | 扩展位置参数,第10个位置变量必须用{}大括号括起来(2位数字以上括起来) |
$$ | 当前所在进程的进程号如echo $$ |
$! | 后台运行的最后一个进程号 PID(当前终端) |
!$ | 调用最后一条命令历史中的参数 |
五、简单的四则运算
运算符号
表达式 | 举例 |
---|---|
$(()) | echo $((1+1)) |
$[] | echo $[10-5] |
expr | expr 10 / 5 |
let | n=1;let n+=1 等价于 let n=n+1 |
[root@localhost ~]# echo $((1+1))
2
[root@localhost ~]# echo $((10-5))
5
[root@localhost ~]# echo $[85*74]
6290
[root@localhost ~]# echo $[10/8] 求商
1
[root@localhost ~]# echo $[10%8] 求余
2
[root@localhost ~]# echo $[8/10] 求商
0
[root@localhost ~]# echo $[8%10] 求余
8
[root@localhost ~]# echo $[5%10] 求余
5
[root@localhost ~]# echo $[10%7] 求余
3
两个程序
[root@localhost ~]# expr 1+1
1+1
[root@localhost ~]# expr 1 + 1
2
[root@localhost ~]# expr 10 - 5
5
[root@localhost ~]# expr 10 * 5
expr: syntax error: unexpected argument “公共”
[root@localhost ~]# expr 10 * 5 #反斜杠转义一下
50
[root@localhost ~]#
[root@localhost ~]# expr 10 / 5
2
[root@localhost ~]# n=1;let n=n+1;echo $n
2
[root@localhost ~]# let n+=2
[root@localhost ~]# echo $n
4
[root@localhost ~]# let n-+2
[root@localhost ~]# echo $n
4
[root@localhost ~]# let n*=2
[root@localhost ~]# echo $n
8
[root@localhost ~]# let n/=2
[root@localhost ~]# echo $n
4
[root@localhost ~]# let n=n**3 #求次幂
[root@localhost ~]# echo $n
64
小数计算
[root@localhost ~]# echo $[1+1.5]
-bash: 1+1.5: 语法错误: 无效的算术运算符 (错误符号是 “.5”) #普通无法计算
[root@localhost ~]# echo 1+1.5|bc #调用bc
2.5
[root@localhost ~]# bc #或者进入bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty’.
1+4.5
5.5
quit
i++ #先赋值,再运算
++i #先运算,再赋值
条件判断语句
条件判断语法格式
- 格式1:test 条件表达式
- 格式2: [ 条件表达式 ]
- 格式3: [[ 条件表达式 ]] 支持正则 =~
特别说明:
①[ 两边有空格 ]
②[[ 两边有空格 ]]
③更多判断,man test 去查看,很多参数都用来进行条件判断
条件判断相关参数
问:你要判断什么?
答:我要判断文件类型,判断文件新旧,判断字符串是否相等,判断权限等等
判断文件类型
判断参数 | 含义 |
---|---|
-e | 判断文件是否存在(任何类型文件) |
-f | 判断文件是否存在并且是一个普通文件 |
-d | 判断文件是否存在并且是一个目录 |
-L | 判断文件是否存在并且是一个软连接文件 |
-b | 判断文件是否存在并且是一个块设备文件 |
-S | 判断文件是否存在并且是一个套接字文件 |
-c | 判断文件是否存在并且是一个字符设备文件 |
-p | 判断文件是否存在并且是一个命名管道文件 |
-s | 判断文件是否存在并且是一个非空文件(有内容) |
判断文件权限
判断参数 | 含义 |
---|---|
-r | 当前用户对其是否可读 |
-w | 当前用户对其是否可写 |
-x | 当前用户对其是否可执行 |
-u | 是否有suid,高级权限冒险位 |
-g | 是否sgid,高级权限强制位 |
-k | 是否有t位,高级权限粘滞位 |
判断文件新旧
说明:这里的新旧是指文件的修改时间
判断参数 | 含义 |
---|---|
file1 -nt file2 | 比较file1是否比file2新 |
file1 -ot file2 | 比较file1是否比file2旧 |
file1 -ef file2 | 比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode |
判断整数
判断参数 | 含义 |
---|---|
-eq | 相等 |
-ne | 不等 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
判断字符串
判断参数 | 含义 |
---|---|
-z | 判断是否为空字符串,字符串长度为0则成立 |
-n | 判断是否为非空字符串,字符串长度不为0则成立 |
string1 = string2 | 判断字符串是否相等 |
string1 = string2 | 判断字符串是否不相等 |
多重条判断
判断符号 | 含义 | 举例 |
---|---|---|
-a 和 && | 逻辑与 | [ 1 -eq 1 -a 1 -ne 0 ] [1-eq 1 ] && [ 1 -ne 0] |
-o 和 || | 逻辑或 | [1 -eq 1 -o 1 -ne 1 ] [ 1-eq 1] |
特别说明:
&& 前面的表达式为真,才会执行后面的代码
|| 前面的表达式为假,才会执行后面的代码
; 只用于分割命令或者表达式
类C数值判断
判断是“==” 两个等于号
赋值是“=” 一个等于号
[root@localhost shell01]# (( 1==1 ));echo $? #判断1是否等于1
0 #结果为0 代表真 结果为1 代表假
[root@localhost shell01]#
[root@localhost shell01]# (( 1>=1 ));echo $?
0
[root@localhost shell01]# (( 1>=2 ));echo $?
1
[root@localhost shell01]# (( 1=2 ));echo $?
-bash: ((: 1=2 : 尝试给非变量赋值 (错误符号是 "=2 ")
1
[root@localhost shell01]# (( A1=2 ));echo $?
0
[root@localhost shell01]# echo $A1
2
[root@localhost shell01]# (( 1!=2 ));echo $? #判断不相等只要一个等号
0
字符串比较
注意:双引号引起来,看作一个整体,= 和 == 在 [ 字符串 ] 比较中都表示判断
[root@localhost shell01]# A=hello;B=world;[ “
A
"
=
"
A" = "
A"="B” ];echo KaTeX parse error: Expected 'EOF', got '#' at position 7: ? 1 #̲判断相等 [root@loca…A" != “$B” ];echo $?
0 #判断不相等
[root@localhost shell01]# A=hello;B=world;[ “
A
"
=
=
"
A" == "
A"=="B” ];echo $?
1
[ ] 和 [[ ]] 的区别
判断字符串为空的时候
一个大括号会出问题(要加双引号)两个没问题(不用加双引号)
判断链接文件是否存在(有效)
[root@localhost shell01]# [ -e test1 -a -L test1 ];echo $?
0
流程控制语句
关键词:选择
1.基本语法结构
if结构
箴言1:只要正确,就一直往前冲
F:表示false 为假
T:表示true 为真
if [ condition ];then
command
command
fi
if test 条件;then
命令
fi
if [[ 条件 ]];then
命令
fi
[ 条件 ] && command
2.if…else 结构
箴言2:分岔路口,二选一
if [ condition ];then
command1
else
comand2
fi
[ 条件 ] && command1 || command2
3.if…elif…else结构
箴言3:选择很多,能走的只有一条
if [ condition1];then
command1 结束
elif [ condition2 ];then
command2 结束
else
command3
fi
注释:
如果条件1满足,执行命令1,如果条件1不满足,再看条件2,如果条件2满足执行命令2后结束;如果条件1和条件2都不满足执行命令3结束。
4.层层嵌套结构
箴言4,多次判断,带你走出人生迷雾
if [ condition1 ];then
command1
if [ condition2 ];then
command2
fi
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
fi
注释:
如果条件1满足,执行命令1,;如果条件2也满足执行命令2,如果不满足就执行命令1结束;如果条件1不满足,不看条件2;直接看条件3,如果条件3满足执行命令3;如果不满足则看条件4,如果条件4满足执行命令4;否则执行命令5
应用案例
1.判断两台主机是否ping通
**需求:**判断当前主机是否和远程主机是否ping通
①思路
1.使用哪个命令实现 ping -c 次数
2.根据命令的执行结果状态来判断是否ping通
3.根据逻辑和语法结构来编写脚本
②落地实现
#!/bin/env bash
#该脚本用于判断当前主机是否和远程指定主机互通
#交互式定义变量,让用户自己决定ping哪个主机
read -p “请输入你要ping的主机IP:” ip
#使用ping程序判断主机是否互通
ping -c1 $ip &>dev/null
if [ $? -eq 0 ];then
echo "当前主机和远程主机$ip是互通的"
else
echo "当前主机和远程主机$ip不通的"
fi
2.判断一个进程是否存在
需求:判断web服务器中httpd进程是否存在
①思路
1.查看进程的相关命令 ps pgrep
2.根据命令的返回状态值来判断进程是否存在
3.根据逻辑用脚本语言实现
②落地实现
#!/bin/env bash
#判断一个程序(httpd)的进程是否存在
pgrep httpd &>/dev/null
test $? -eq 0 && echo "当前httpd进程存在" || echo "当前httpd进程不存在"
3.判断一个服务器是否正常
需求:判断门户网站是否能够正常访问
①思路
1.可以判断进程是否存在,用/etc/init.dhttpd status 判断状态等方法
2.最好的方法是直接去访问一下,通过访问成功和失败的返回值来判断
linux环境,wget (优先选择)curl elinks -dump 都是没有交互的
②落地实现
#!/bin/env bash
#判断门户网站是否能正常提供服务
#定义变量
web_server=www.itcast.cn#或者写地址
wget -P /shell02/ $web_server &>/dev/null
[ $? -eq 0 ] && echo "当前网站ok" && rm -f /shell02/index.* || echo "当前网站服务不ok,请立刻处理"
grep -w 精确匹配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WD9w7Gvo-1677225141897)(C:\Users\57736\AppData\Roaming\Typora\typora-user-images\image-20210906113316491.png)]
for循环语句
关键词:爱的魔力转圈圈
列表循环
列表for循环:用于将一组命令执行已知次数
基本语法格式
for variable in {list}
do
command
command
...
done
或者
for variable in a b c
do
command
command
done
#!/bin/env bash
#...
for i in {1..199}
do
echo $hello 666
done
不带列表循环
不带列表循环的for循环执行时由用户指定参数和参数的个数
基本语法格式
for variable
do
command
command
...
done
类C风格的for循环
for((expr1;expr2;expr3;))
do
command
command
...
done
for (( i=1;i<=5;i++ ))
do
echo $i
done
# expr1:定义变量并赋初值
# expr2:决定是否进行循环(条件)
# expr3:决定循环变量如何改变,决定循环什么时候退出
应用案例
脚本计算1-100奇数和
①思路
1.定义一个变量来保存奇数的和 sum=0
2.找出1-100的奇数,保存到另一个变量里 i=遍历出来的奇数
3.从1-100中找出奇数后,再相加,然后将和赋值给变量 循环变量 for
4.遍历完毕后,将sum的值打印出来
②落地实现(条条大路通罗马)
#!/bin/env bash
# 计算1-100的奇数和
# 定义变量来保存奇数和
sum=0
# for循环遍历1-100的奇数,并且相加,把结果重新赋值给sum
for i in {1..100..2}
do
let sum=$sum+$i
done
# 打印所有奇数和
echo "1-100的奇数和是:$sum"
判断所输入整数是否为质数
①思路
1.让用户输入一个数,保存到一个变量里
2.如果能被其数整除就不是质数–> n u m num% numi 是否等于0 $i=2 到num-1
3.如果输入的数是1或者2取模根据上面判断又不符合。所以先排除1和2
4.测试序列从2开始,输入的数是4–>得出结果 n u m 不能和 num不能和 num不能和i相等,并且$num不能小于$1
②落地实现
#!/bin/bash
read -p "请输入一个正整数字:" number
[ $number -eq 1 ] && echo "$number不是质数" && exit
[ $number -eq 2 ] && echo "$number是质数" && exit
#循环判断用户所输入的数字是否为质数
for i in `seq 2 $[$number-1]`
do
[ $[number%i] -eq 0 ] && echo "$number不是质数" && exit
done
echo "$number是质数" && exit
批量创建用户
需求:批量加5个用户,以u1到u5命名,并同一加一个新组,组名为class,统一改密码为123
①思路
1.添加用户命令 useradd -G class
2.判断class组是否存在 grep -w ^class /etc/group 或者groupadd calss
3.根据题意,判断该脚本循环5次来添加用户 for
4. 给用户设置密码 应该放到循环体里面
②落地实现
#!/bin/env bash
#判断class组是否存在
grep -w ^class /etc/group &>/dev/null
test $? -ne 0&& groupadd class
#循环创建用户
for ((i=1;i<=5;i++))
do
useradd -G calss u$i
done