文章目录
引言
在一些复杂的 Linux 维护工作中,大量重复性的输入和交互操作不但费时费力,而且容易出错,而编写一个恰到好处的 Shell 脚本程序,可以批量处理,自动化的完成一系列维护任务,大大减轻管理员的负担。
一、Shell 脚本编程规范
1.概述
Shell 脚本就是将要执行的命令按顺序保存到一个文本中,并给该文件可执行权限,方便一次性执行的一个程序文件。主要是方便管理员进行设置或管理,可结合各种 Shell 控制语句以完成更复杂的操作
2.应用场景
-
Shell 脚本常用于重复性操作、批量事务处理、自动化运维、服务运行状态监控、定时任务执行等。
-
像网站发布脚本,每天登录网站,我们会发现每页内容,并不是一成不变的,正常情况下网站会根据开发人员开发完成的代码定期更新网站内容,称之为网站定期发布新版本。
-
但是对于一些更新间隔比较短的网站,手动执行命令发布是一种重复性的操作,很浪费时间,又比较麻烦。为解决此问题可以开发一个自动发布脚本,就可以高效准确、轻松自如地一键发布脚本了。
3. Shell 作用
-
Shell是 一个命令解释器,它在操作系统的最外层,负责直接与用户进行对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕反馈给用户。
-
这种对话方式可以是交互也可以是非交互式的,我们所输入的命令计算机是不识别的,这时就需要一种程序来帮助我们进行翻译,变成计算机能识别的二进制程序,同时又把计算机生成的结果返回给我们。
-
常见的 Shell 解释器程序有很多种,使用不同的 Shell 时,其内部指令、命令行提示符等方面会存在一些区别。
-
通过 /etc/shells 文件可以了解当前系统所支持的 Shell 脚本种类,如下
[root@localhost ~]#cat /etc/shells
/bin/sh #是bash命令的软链接(已经被/bin/bash所替换)
/bin/bash #基准于GNU的框架下发展出的Shell
/sbin/nologin
/usr/bin/sh #已被bash替换
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh #己经被/bin/ bash所替换
4.Shell 脚本的构成
简单地说,只要将平时使用的各种 Linux 命令按顺序保存到一个文本文件,然后添加可执行权限,这个文件就成为了一个 Shell 脚本了。
- 例如,执行以下操作就可以创建第一个脚本文件:first.sh
[root@localhost ~]#vim first.sh
#!/bin/bash #特殊的脚本声明,表示此行以下的代码语句是通过/bin/bash程序来执行
cd /boot
pwd
ls -lh vml*
- 添加可执行的权限,操作如下
[root@localhost ~]#chmod +x first.sh
- 执行脚本文件
[root@localhost ~]# ./first.sh #绝对路径和相对路径的方式,必须要有x权限
[root@localhost ~]#sh first.sh #sh脚本文件路径
[root@localhost ~]#source first.sh #source脚本文件路径,也可以通过“.”来执行,可以没有x权限
[root@localhost ~]#sh < first.sh
或
[root@localhost ~]#cat first.sh |sh (bash)
5.如何完善脚本结构
-
一个合格的 Shell 脚本程序应该遵循标准的脚本结构
-
应当能够输出友好的提示信息、更容易被人读懂
-
对于代码较多、结构复杂的脚本,应添加必要的注释文字,已便自己跟他人都能轻松理解
例如,下面被完善后的脚本,输出信息更容易被读懂
[root@localhost ~]#vim first.sh
#!/bin/bash
#this is my first Shell-Script.
cd /boot
echo "当前的目录位于 :"
pwd
echo "其中以vml开头的文件包括 :"
ls -lh vml*
[root@localhost ~]#./first.sh
当前的目录位于 :
/boot
其中以vml开头的文件包括 :
-rwxr-xr-x. 1 root root 5.7M 8月 11 08:54 vmlinuz-0-rescue-821a021ce0074800a769d52441ec6aca
-rwxr-xr-x. 1 root root 5.7M 8月 23 2017 vmlinuz-3.10.0-693.el7.x86_64
6.重定向和管道操作
由于 Shell 脚本“批量处理”的特殊性,其大部分操作过程位于后台,不需要用户进行干预。因此学会提取、过滤执行信息变得十分重要。
6.1 重定向操作
Linux 系统使用文件来描述各种硬件、设备等资源,比如硬盘和分区、光盘等设备文件。用户通过处理操作系统处理信息的过程中,包括以下几类交互设备文件。
-
标准输入:
默认的设备是键盘,设备文件为 /dev/stdin ,文件编号为0,命令将从标准输入文件中读取在执行过程中需要的输入数据。 -
标准输出:
默认的设备是显示器,设备文件为 /devlstdout ,文件编号为1,命令将执行后的输出结果发送到标准输出文件。 -
标准错误:
默认的设备是显示器,设备文件为 /dev/stderr ,文件编号为2,命令将执行期间的各种错误信息发送到标准错误文件。
三种设备文件默认使用键盘和显示器作为关联的设备,与操作系统进行交互,完成最基本的输入、输出操作。从键盘接收用户输入的各种命令字串、辅助控制信息,并将命令结果输出到屏幕上;如果命令执行出错,也会将错误信息反馈到屏幕上。
6.1.1 重定向输入
-
重定向输入指的是将命令中接收输入的途径由默认的键盘改为指定的文件,而不是等待从键盘输入。(使用"<"操作符)
-
通过重定向输入可以使一些交互式操作能够通过读取文件来完成。例如,使用 passwd 命令为用户设置密码时,每次都要根据提示输入两次密码,十分麻烦,如果改用重定向输入将可以省略交互式的过程,从而自动完成密码设置:
[root@localhost /home]#vim pass.txt
123456 #输入密码串内容"123456"
wq保存并退出
[root@localhost /home]#passwd --stdin gulei < pass.txt #从pass.txt文件中获取密码
更改用户 gulei 的密码 。
passwd:所有的身份验证令牌已经成功更新。 #更改密码成功
没有交互式的操作语句更方便在 Shell 脚本程序中使用,可以大大减少程序被打断的过程,提高脚本执行的效率。
6.1.2 重定向输出
-
重定向输出指的是将命令的正常输出结果保存在指定的文件中,而不是直接显示在显示器的屏幕上。
-
重定向输出使用 “>“或”>>” 操作符号,分别用于覆盖或追加文件
-
若重定向输出的目标文件不存在,则会新建该文件,然后将前面命令的输出结果保存到该文件中;若目标文件已经存在,则将输出结果覆盖或追加到文件中。
例如,将当前主机的 CPU 类型信息保存在 kernel.txt 文件中,而不是直接显示在屏幕上,操作如下
[root@localhost /home]#uname -p > kernel.txt
[root@localhost /home]#cat kernel.txt
x86_64
当需要保留目标文件原有的内容时,应使用 ">>"符号,以便追加内容而不是全部覆盖。例如,将内核版本信息追加到 上面的 kernel.txt 文件中,操作如下:
[root@localhost /home]#uname -r >>kernel.txt
[root@localhost /home]#cat kernel.txt
x86_64
3.10.0-693.el7.x86_64
6.1.3 错误重定向
-
错误重定向指的是将执行命令过程中出现的错误信息(如选项或参数错误等)保存到指定的文件,而不是直接显示在屏幕上。
-
错误重定向使用 “2>” 操作符,其中 “2”是指错误文件的标号
-
在使用标准输出、标准输入重定向时,实际上省略了1和0的编号
-
在实际应用中,错误重定向用来收集程序执行的错误信息,为排错提供了依据
-
对于 Shell 脚本来说,还可以把无关紧要的错误信息重定向到空文件 /dev/null 中,能够保持脚本输出的简洁
[root@localhost /home]#echo abc > /dev/null #输入一个abc进去
[root@localhost /home]#cat /dev/null
[root@localhost /home]# #里面没有内容
-
使用 “2>” 操作符时,会像使用 “>” 一样覆盖目标文件的内容,如果想要追加输入,应改为 “2>>” 操作符
-
当命令输出的结果可能既包括标准输出信息,又包括错误输出信息时,可以使用操作符 “>” "2>"将两类输出信息分别保存到不同的文件,也可以使用 "&>"操作符将两类输出信息保存到同一个文件
[root@localhost /home]#ls /etc/passwd xxx &>> 1.txt #将错误显示的内容和正确显示的内容分开
[root@localhost /home]#cat 1.txt
ls: 无法访问xxx: 没有那个文件或目录
/etc/passwd
6.2 管道操作
-
管道操作为不同命令之间的协同工作提供了一种机制,位于管道符号 “|” 左侧的命令输出的结果,将作为右侧命令的输入(处理对象),同一行命令中可以使用多个管道。
-
在 Shell 脚本应用中,管道操作通常用来过滤所需要的关键信息
案例①,使用 grep 命令查询使用 “/bin/bash” 作为 Shell 的用户名称时,会输出符合条件的整行内容,在此基础上可以结合管道操作与 awk 命令做进一步过滤,只输出用户名和登录 Shell 列,操作如下:
[root@localhost ~]#grep "/bin/bash$" /etc/passwd
root:x:0:0:root:/root:/bin/bash #提取之前
gulei:x:1000:1000:GuLei:/home/gulei:/bin/bash
[root@localhost ~]#grep "/bin/bash$" /etc/passwd | awk -F: '{print $1,$7}'
root /bin/bash
gulei /bin/bash #提取之后
(awk 命令的作用时以冒号 “:” 作为分隔,输出第1和第7个区域的字符串,-F 部分用来指定分隔符号)
案例②,提取根分区的磁盘使用率信息,可以执行下面的操作:
[root@localhost ~]#df -h #提取之前
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root 56G 8.1G 48G 15% /
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 9.0M 1.9G 1% /run
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/sda1 497M 172M 326M 35% /boot
tmpfs 378M 0 378M 0% /run/user/0
tmpfs 378M 12K 378M 1% /run/user/42
[root@localhost ~]#df -Th |grep "/$"| awk '{print $6}' #"/$"表示以/结尾的行
15% #提取之后
二、Shell 脚本变量解析
1. Shell 变量的作用
-
Shell 变量是用来存放系统和用户需要使用的特定参数(值),而且这些参数可以根据用户的设定或系统环境的变化而相应变化
-
通过使用变量,Shell 程序能够提供更加灵活的功能,适应性更强。
2. Shell 变量类型
常见的 Shell 变量的类型包括自定义变量、环境变量、位置变量、预定义变量
3. 自定义变量
3.1 简述
-
自定义变量是由系统用户自定义的变量,只在用户自己的 Shell 环境中有效,因此又称为本地变量。
-
在编写 Shell 脚本时,通常会设置一些特定的自定义变量,以适应程序执行过程中的各种变化,满足不同的需求。
3.2 变量的定义
-
Bash 中的变量操作相对比较简单,不像其他高级编程语言,如 C/C++、Java 等那么复杂
-
在定义一个新的变量时,一般不需要提前进行声明,而是直接指定变量名称并赋给初始值(内容)即可
-
定义变量的基本格式为 “变量名=变量值”
-
变量名以字母或下划线开头,名称中不能包含特殊字符,区分大小写,建议全大写,方便辨识,等号两边不能有空格
3.3 查看和引用变量的值
-
通过在变量名称前添加符号 “$” ,可以引用一个变量的值
-
使用 echo 命令可以查看变量,可以在一条 echo 命令中同时查看多个变量值
[root@localhost ~]#echo $name
[root@localhost ~]#name=lisi
[root@localhost ~]#test=22
[root@localhost ~]#echo $name $test
lisi 22
- 变量的名称容易和紧跟其后的其他字符相混淆,我们可以使用大括号"{ }"将其括起来,否则将无法确定正确的变量名称,对于未定义的变量,显示为空值
[root@localhost ~]#echo $name6.6
.6
[root@localhost ~]#echo ${name}6.6
lisi6.6
3.4 变量赋值的特殊操作
在等号 “=” 后面直接指定变量内容是为变量赋值最基础的方法,除此之外,还可以使用一些特殊的赋值操作。
- 双引号:允许通过$符号引用其他变量值
[root@localhost ~]#unset py #取消变量名
[root@localhost ~]#echo $py
[root@localhost ~]#py="python $version"
[root@localhost ~]#echo $py
python 2
- 单引号:禁止引用其他变量值,$视为普通字符
[root@localhost ~]#version=2
[root@localhost ~]#py='python $version'
[root@localhost ~]#echo $py
python $version #无法调用变量
- 反撇号:命令替换,提取命令执行后的输出结果
[root@localhost ~]#ls -lh which useradd
ls: 无法访问which: 没有那个文件或目录
ls: 无法访问useradd: 没有那个文件或目录
[root@localhost ~]#ls -lh `which useradd`
-rwxr-x---. 1 root root 116K 11月 6 2016 /usr/sbin/useradd
#上面操作相当于连续执行了两条命令,先通过 which useradd 命令查找useradd 的位置,然后根据查找结果列出文件属性。
执行过程中会用 which useradd 命令的输出结果替换整个反撇号范围。
- 因为反撇号难以在一行命令中实现嵌套命令替换操作,这时可以改用“$()”来代替反撇号操作,以解决嵌套的问题。
#查询提供useradd命令程序的软件包所安装的配置文件位置
[root@localhost ~]#rpm -qc $(rpm -qf $(which useradd))
/etc/default/useradd
/etc/login.defs
- read命令
read 命令用来提示用户输入信息,从而实现简单的交互过程。 执行时将从标准输入设备(键盘)读入一行内容,并以空格为分隔符,将读入的各字段依次赋值给指定的变量(多余的内容赋值给最后一个变量)。
若指定的变量只有一个,则将整行内容赋值给此变量。
案例①
执行下面的操作将会等待用户输入文字,并将输入的内容赋值给变量 test
[root@localhost ~]#read test
123123
[root@localhost ~]#echo $test
123123
为了让交互操作的界面更加友好,read 命令可以结合"-p"选项来设置提示信息,以便告诉用户应该输入什么内容。
案例②
[root@localhost ~]#read -p "请输入您的姓名:" name
请输入您的姓名:奥斯特洛夫斯基
[root@localhost ~]#echo $name
奥斯特洛夫斯基
[root@localhost /home]#echo 192.168.2.128 > ip.txt
[root@localhost /home]#cat ip.txt
192.168.2.128
[root@localhost /home]#read -p "input your ip:" IP < ip.txt
[root@localhost /home]#echo $IP
192.168.2.128
#从文件中读取内容,提取给变量,省去了交互的过程
3.5 设置变量的作用范围
- 默认情况下,新定义的变量只在当前 Shell 环境中有效,因此称为局部变量。当进入子程序或新的子 Shell 环境时,局部变量将无法再使用。
[root@localhost /home]#name=lisi
[root@localhost /home]#test=20
[root@localhost /home]#bash #进入shell环境
[root@localhost /home]#echo $name $test
#无法调用父shell环境中的变量
- 可以通过内部命令 export 将指定的变量导出为全局变量,使用户定义的变量在所有的子 Shell 环境中能够继续使用
[root@localhost /home]#exit #返回原有的shell环境
exit
[root@localhost /home]#export name test #将name test设为全局变量
[root@localhost /home]#echo $name $test
lisi 20
[root@localhost /home]#bash #进入shell环境
[root@localhost /home]#echo $name $test
lisi 20 #可以调用
3.6 数值变量的运算
在Bash Shell 环境中,只能进行简单的整数运算,不支持小数运算,整数值的运算主要通过内部命令 expr 进行,
运算符与变量之间必须有至少一个空格。
基本格式如下:
expr 变量1 运算符 变量2 [运算符 变量3]...
常用的几种运算符如下表
运算符 | 说明 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取余 |
[root@localhost /home]#X=55
[root@localhost /home]#Y=15
[root@localhost /home]#expr $X + $Y
70
[root@localhost /home]#expr $X - $Y
40
[root@localhost /home]#expr $X / $Y
3
[root@localhost /home]#expr $X * $Y #乘法运算不能仅用 "*" 否则会被当成通配符
expr: 语法错误
[root@localhost /home]#expr $X "*" $Y
825
[root@localhost /home]#expr $X '*' $Y
825
[root@localhost /home]#expr $X \* $Y
825
[root@localhost /home]#expr $X % $Y
10
案例
写一个简易的运算脚本,操作如下:
[root@localhost /home]#vim XYXY.sh
#!/bin/bash
#1.定义输出数字
read -p "请输入第一个数字:" num1
read -p "请输入第二个数字:" num2
#2.执行加法运算
sum=`expr $num1 + $num2`
echo "求和数:$sum"
[root@localhost /home]#chmod +x XYXY.sh
[root@localhost /home]#./XYXY.sh
请输入第一个数字:22
请输入第二个数字:88
求和数:110
4. 特殊变量
4.1 环境变量
-
环境变量因为运行所需要而由系统提前创建的一类变量,主要用来用来设置用户的工作环境,包括用户宿主目录、命令查找路径,用户当前目录、登录终端等。
-
环境变量的值由 Linux 系统自动维护,会随着用户状态的改变而改变。
-
配置文件:/etc/profile、~/.bash_profile
-
使用 env 命令可以查看当前工作环境下的环境变量
[root@localhost ~]#env
XDG_SESSION_ID=2
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
SSH_CLIENT=192.168.8.1 2532 22
SSH_TTY=/dev/pts/0
USER=root
name=lisi
......下面省略
- PATH 变量用于设置可执行的默认搜索路径,当仅指定文件名称来执行命令程序时,Linux 系统将在 PATH 变量指定的目录范围查找对应的可执行文件,如果找不到则会提示 “command not found”
例如,XYXY脚本位于 home 目录下,如果希望能直接通过文件名来运行脚本,可以修改 PATH 变量以添加搜索路径,或者将这个脚本复制到现有搜索路径的某个文件夹下
[root@localhost /home]#ls -lh ./XYXY.sh #确认脚本的位置
-rwxr-xr-x 1 root root 193 9月 3 22:29 ./XYXY.sh
[root@localhost /home]#echo $PATH #查看当前搜索路径
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost /home]#XYXY.sh #直接以文件名执行
bash: XYXY.sh: 未找到命令... #失败,找不到命令
[root@localhost /home]#PATH="$PATH:/home" #将/home添加进搜索路径
[root@localhost /home]#echo $PATH #查看是否添加成功
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/home
[root@localhost /home]#XYXY.sh #以文件名运行脚本成功
请输入第一个数字:1
请输入第二个数字:2
求和数:3
4.2 只读变量
用于变量值不允许被修改的情况
[root@localhost /home]#shuzi=123456
[root@localhost /home]#echo $shuzi
123456
[root@localhost /home]#shuzi=123
[root@localhost /home]#echo $shuzi
123
[root@localhost /home]#readonly $shuzi
bash: readonly: `123': 不是有效的标识符
[root@localhost /home]#readonly shuzi
[root@localhost /home]#echo $shuzi
123
[root@localhost /home]#shuzi=123456 #无法被修改
bash: shuzi: 只读变量
[root@localhost /home]#unset shuzi #无法取消
bash: unset: shuzi: 无法反设定: 只读 variable
4.3 位置变量
-
当执行命令行操作时,第一个字段表示命令名或脚本程序名,其余的字符串参数按照从左到右的顺序依次赋值给位置变量。
-
位置变量也称为位置参数,使用$1 、$2 、$3 …$9表示
[root@localhost /home]#vim add2num.sh
#!/bin/bash
SUM=`expr $1 + $2`
echo "$1 + $2 = $SUM"
[root@localhost /home]#chmod +x add2num.sh
[root@localhost /home]#./add2num.sh 9 9
9 + 9 = 18
[root@localhost /home]#vim user.sh #编写脚本
[root@localhost /home]#chmod +x user.sh #添加权限
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
wq保存退出
[root@localhost /home]#./user.sh zhangsan 123456 #输入用户和密码
更改用户 zhangsan 的密码 。
passwd:所有的身份验证令牌已经成功更新。 #创建zhangsan用户成功
4.4 预定义变量
-
预定义变量是由 Bash 程序预先定义好的一类特殊变量,用户只能使用预定义变量,无法创建新的预定义变量,也不能直接为预定义变量赋值
-
使用 “$” 符号和另一个符号组合表示,常用的几个预定义变量及含义如下所示:
变量 | 说明 |
---|---|
$# | 命令行中位置变量的个数 |
$* | 所有位置变量的内容 |
$? | 上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错 |
$0 | 当前执行的进程/程序名 |
$$ | 表示返回当前进程的进程号 |
$ ! | 返回最后一个后台进程的进程号 |
$@ | 表示列出所有位置参数,但是是以单个的形式的列出 |
[root@localhost /home]#ls /tmp/
ssh-lAEJdk0bXFHK
systemd-private-6ffb0e7f9a9f4cfe90c899462445f756-colord.service-rE8567
tracker-extract-files.0
yum_save_tx.2021-09-03.11-55.MXHz3Z.yumtx
[root@localhost /home]#echo $?
0 #0表示执行正常
[root@localhost /home]#ls /tmp/11
ls: 无法访问/tmp/11: 没有那个文件或目录
[root@localhost /home]#echo $?
2 #2表示执行异常
总结
-
重定向与管道操作是 Shell 环境中十分常用的功能,如果能够熟练的掌握并灵活的运用,将有助于编写代码简洁但功能强大的 Shell 脚本程序。
-
在使用 expr 进行计算的时候,变量必须是整数,不能是字符串,也不能含小数,否则会出错。