Shell的定义
1.概念
Shell 也是一种程序设计语言,它有变量,关键字,各种控制语句,语法结构,利用shell程序设计语言可以编写功能很强、代码简短的程序。
我们可以通过shell的命令来控制和操作操作系统,比如linux中的shell命令就包括ls、cd、pwd等等,总结来说shell就是一个命令解释器,他通过接收用户输入的shell命令来启动、停止程序的运行或者对计算机进行控制。
解释型语言
2.分类
命令操作:
cat /etc/shells #查看shell的种类
echo $SHELL #查看当前正在使用的shell
Shell的种类:
/bin/sh
/bin/bash <-----------最常用
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
Shell的登录种类修改:
vim /etc/passwd #修改用户登录的shell
3.Shell的使用范围
- 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...)---初始化脚本
- 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)---自动化安装脚本
- 日志分析处理程序(PV, UV, 200, !200,grep/awk)----akw、sed、grep
- 自动化备份恢复程序(MySQL完全备份/增量 + Crond)-----数据备份恢复脚本
- 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)--监控脚本
- 配合Zabbix监控信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,Apache,MySQL)
- Shell可以做任何运维的任务(一切取决于业务需求)
4.bash初始化
登录脚本bash配置文件
全局配置
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置
~/.bsah_profile
~/.bashrc
登录时加载bash配置文件的过程
~/.bash_profile --> ~/.bashrc --> /etc/bashrc ---> /etc/profile --> /etc/profile.d/*.sh
tips:
/etc/profile(系统级)启动时执行,且检查并执行/etc/profile.d/*.sh的脚本
~/.bash_login(用户级)登录时执行,登陆后必须执行里面的命令
~/.bash_logout(用户级)离开时执行,离开时执行如果想在注销shell前执行一些工作,都可以在此文件中设置
Shell编程
1.Shell规范
#!/bin/bash ------------shebang蛇棒, 解释器, 翻译功能
#
# Author: soso666
# Email: soso666@163.com
# Github: https:github.com/soso666
# Date: 2024/8/1 ------------注释
... -------------shell代码
命令操作
sh -x [脚本路径] [脚本参数] #将执行该脚本并显示所有变量的值
sh -n [脚本路径] [脚本参数] #不执行脚本只是检查语法模式,将返回所有错误语法
sh -v [脚本路径] [脚本参数] #执行脚本前把脚本内容显示在屏幕上
2.Shell的变量类型
变量种类
预定义变量、环境变量、自定义变量、位置变量
预定义变量
概念:
预定义的特殊变量有着特殊的含义,用户不可以更改,所有的预定义变量都由$符号和另外一个符号组成,常用的预定义特殊变量
$$ 当前进程PID
$! 上一个后台进程的PID (wait命令中使用,后面讲)
$? 命令执行后的返回状态:0 为执行正确,非 0 为执行错误
$# 位置参数的数量
$* 所有位置参数的内容 (将参数作为一个整体来进行显示)
$@ 显示所有的参数(显示一个一个参数)
$0 表示脚本的执行路径
自定义变量
概念:
就是自己设置的变量只能在当前终端和脚本中使用
定义语法:
[变量名称]=[变量值]
使用语法:
$[变量名称]
${[变量名称]}
当前shell终端中
set [变量名称]=[变量值] 设置变量
unset [变量名称] 删除变量
环境变量
概念:
shell在开始执行时已经定义好的,就是系统执行环境的一些设置
设置语法:
(1)临时设置
[环境变量名]=[值]
IPADDR(环境变量名)=192.168.252.145 临时设置
(2)永久设置
放在/etc/profile.d/目录下
vim [脚本名].sh
生效
source /etc/profile.d/[脚本名].sh
常用环境变量
USER
UID
HOME
HOSTNAME
PWD
PATH
位置变量
位置变量也叫位置参数,在脚本代码中调用通过命令行传递给脚本的参数
位置参数:$1,$2,$3,$4,$...
[脚本名].sh [参数1] [参数2] [参数3] [参数4] ....
^ ^ ^ ^
| | | |
sh文件中 $1 $2 $3 $4 ....
3.变量运算
算式运算符
+、-、*、/、()、%取余
算式方法
$(([表达式]))
$[ 表达式 ]
expr $[变量] [运算符] $[变量]
浮点运算:
echo "scale=[精度];[算数表达式]" | bc -l
-l 表示标准数学库
scale=[精度]表示保留几位小数
tips:
乘法:
expr $[变量] \* $[变量]
expr $[变量] '*' $[变量]
脚本常用的方式:
[命令]`[算式表达式]`
4.变量引用
转义:\
当一个字符被引用时,其特殊含义被禁止,把有意义的变的没意义,把没意义的变的有意义
完全引用:' ' #强引 #指的是被引号包围起来的变量名不会进行不会进行解析,原样变量名原样输出,这种方式比较适合定义显示纯字符串的情况,不希望解析变量、命令等的场景。
部分引用:" " #弱引 #指的是被引号包围起来的变量名会先进行解析,然后将变量的解析结果输出来。这种方式适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。
1.读取用户标准输入:read
read -t [秒数] -p "[提示信息]" [存放标准输入的变量名]... -s
tips:
-t 指定多长时间后输入消失
-p 指定提示信息
-s 屏蔽回显,一般输入的内容需要保密
取消屏幕回显
stty -echo 取消屏幕回显
stty echo 恢复回显
显示变量长度
${# [变量名]} 显示变量长度
脚本运行
添加执行权限执行
chmod +x [脚本].sh
./[脚本].sh
bash [脚本的绝对路径]
改变shell环境执行脚本
source [脚本的绝对路径]
5.变量替换
${[变量名]:-[数字]}
1.变量值为空
[数字]代替[变量名]进行赋值,[变量名]仍然为空
2.变量值不为空
[变量名]直接赋值,[变量名]值不变
${[变量名]:+[数字]}
1.变量值为空
[变量名]直接赋值为空,[变量名]值不变
2.变量值不为空
[数字]代替[变量名]进行赋值,[变量名]值不变
${[变量名]:=[数字]}
1.变量值为空
[数字]直接赋值,[数字]也给[变量名]赋值
2.变量值不为空
[变量名]直接赋值,[变量名]值不变
索引和切片
${[变量名]:[数字]} 将[变量名]的值从第[数字]位开始截取
${[变量名]:[数字1]:[数字2]} 将[变量名]的值从第[数字1]位开始,截取[数字2]位
${[变量名]:[数字1]:-[数字2]} 将[变量名]的值从第[数字1]位开始,截取到倒数第[数字2]位
匹配截取
${变量#关键词} 从开头开始往后匹配到第一个关键字进行截取,显示关键字以后的内容
${变量##关键词} 从开头开始往后匹配到最后一个关键字进行截取,显示关键字以后的内容
${变量%关键词} 从最后开始往前匹配到第一个关键字进行截取,显示关键字前面的内容
${变量%%关键词} 从最后开始往前匹配到最后一个关键字进行截取,显示关键字前面的内容
${变量/旧字符串/新字符串} 变量中匹配内容符合旧字符串,然后将新字符串替换匹配到的第一个符合的旧字符串
${变量//旧字符串/新字符串} 变量中匹配内容符合旧字符串,然后将新字符串替换匹配到的所有符合的旧字符串
basename和dirname
basename '[文件路径]' 只显示文件名去除目录路径
dirname '[文件路径]' 只显示目录路径去除文件名
6.流程控制
条件结构
if [ 条件1 ]; then
[执行的语句]
elif [ 条件2 ]; then
[执行的语句]
...
else
[执行的语句]
fi
tips:
算术运算符:
+ 加
- 减
* 乘
/ 除
% 取余
= 赋值
== 相等 相同则返回 true
!= 不相等 不相同则返回 true
关系运算符:
-eq 检测两个数是否相等
-ne 检测两个数是否不相等
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
布尔运算符:
! 非运算
-o 或运算
-a 与运算
逻辑运算符:
&& and
|| or
字符串运算符:
= 比较字符串是否相等
!= 比较字符串是否不相等
=~ 匹配正则表达式,=~:正则匹百配,用来判断其左侧的参数是否符合右边的要求
-z 检测字符串长度是否为0
-n 检测字符串长度是否不为0
$[变量] 检测字符串是否不为空
检测是否存在:
-f 存在且是正规文件
-d 存在且是目录
-h 存在且是符号链接
-b 块设备
-c 字符设备
-e 文件存在
[]和[[]]的区别:
1.[]几乎可以运行在所有的Shell解释器中,[[ ]]仅可运行在特定的几个Shell解释器中
2.<和>在[[ ]]中用作排序,而[ ]不支持
3.[ ]中使用-a和-o表示逻辑与和逻辑或,[[ ]]使用&&和||来表示
4.在[ ]中==是字符匹配,在[[ ]]中是模式匹配
5.[ ]不支持正则匹配,[[ ]]支持用=~进行正则匹配
6.[ ]仅在部分Shell中支持用()进行分组,[[ ]]均支持
7.[ ]中如果变量没有定义,那么需用双引号引起来,[[ ]]中不需要
test测试:
语法:
test [命令参数] [条件] 条件为真返回0 条件为假返回1
参数:
-f 存在且是正规文件
-d 存在且是目录
-h 存在且是符号链接
-b 块设备
-c 字符设备
-e 文件存在
-n 字符串不为0为真
-z 字符串是0为真
case选择
case $[变量] in
[模式1]):
[执行的命令序列]
;;
[模式2]):
[执行的命令序列]
;;
...
[模式n]):
[执行的命令序列]
;;
*):
[执行的命令序列]
;;
esac
tips:
当变量值等于某个模式时便执行下面的命令序列
当变量值与所有的模式都不匹配时执行*)下面的命令序列
in代表从下方的各个模式进行匹配
循环结构
for循环
for i in {取值范围} for((i=[值1];[条件];i++))
do do
[循环体] [循环体]
done done
tips:
i++ 先赋值再运算
++i 先运算再赋值
let i++
let ++i
seq 打印序列号,只跟数字
seq 命令用于产生从某个数到另外一个数之间的所有整数。
seq命令的原理就不说了,这里说说为什么不能在{ }中使用变量。其实原因写在bash的man手册中:
大意是说,Bash中会最先展开{ }中的内容,这个时候$NUM还不会被具体的值替代,所以是i在循环中读取的是‘{1..$NUM}’的一个完整的字符串,输出时$NUM会被10替代,就有了'{1..10}'这样的结果。
`seq -w $num` 数字从一开始到$num
$(seq -1 $num) 数字从一开始到$num
while循环
while [ 条件 ]
do
[循环体]
done
tips:
done < [文件名] 可以从文件中读取数据适用于
while read line
do
echo $line
done < [文件名]
循环显示文件中每一行的数据
当条件为真的时候执行循环体
until循环
until [条件]
do
[循环体]
done
tips:
当条件为假的时候执行循环体
循环控制
shift、continue、break、exit
shift命令:
shift [数字] 将参数位置向左移[数字]个
continue命令:
在循环中不执行continue下面的代码,转而进入下一轮循环
exit命令:
退出脚本,exit 0
break命令:
结束并退出本次循环
7.函数
function
设置函数(无参):
[function] [function_name] (){
[执行命令列表]
return [返回值] -------可选
}
[function_name]
取消设置函数:
unset [function_name]
tips:
function_name 生效
unset function_name
function_name 不生效
设置函数(有参数):
[function] [function_name] (){
[执行命令列表]
$1 $2 $3...
return [返回值] -------可选
}
[function_name] $1 $2 $3...
数组
普通数组:只能用整数作为数组的索引
关联数组:可以使用字符串作为数组的索引
普通数组:
[数组名] = ([元数1] [元数2] ...)
${数组名[index]}
关联数组:
方式一:
declare -A [数组名]
[数组名]=([索引]=[值] ...)
方式二:
declare -a [数组名]=([值] ...)
方式三:
[数组名]=([值] ...)
访问数组:
${数组名[索引]} 通过索引访问单个元数
${数组名[@]} 访问所有元数(一个一个显示)
${数组名[*]} 访问所有元数(单个字符串显示)
${#数组名[@]} 统计元数个数
${!数组名[@]} 统计索引
tips:
*与@的区别
[*]将访问的元数作为字符串输出
[@]将访问的元数一个一个输出
8.正则表达式
正则表达式基本元字符
元字符 | 功能 | 语法(love) |
^ | 行首定位符 | ^love |
$ | 行尾定位符 | love$ |
. | 匹配单个字符 | l.ve |
* | 匹配前导符0-多次 | ab*love |
.* | 匹配任意字符 | lo.* |
[] | 匹配方括号中的任意一个字符 | [lL]ove |
[ - ] | 匹配指定范围内的一个字符 | [a-z0-9]ove |
[^] | 匹配不在指定组内的字符 | [^a-z0-9]ove |
\ | 用来转义元字符 | love\. |
\< | 词首定位符 | \<love |
\> | 词尾定位符 | love\> |
\(\) | 匹配后的标签 |
扩展正则表达式元字符
元字符 | 功能 | 语法(love) |
+ | 匹配一个或多个前导字符 | [a-z]+ove |
? | 匹配零个或一个前导字符 | lo?ve |
[字符1]|[字符2] | 匹配[字符1]或[字符2] | love|hate |
(..)(..)\1\2 | 标签匹配字符 | (love)able\1er |
x{m} | 字符x重复m次 | o{5} |
x{m,} | 字符x重复至少m次 | o{5,} |
x{m,n} | 字符x重复m到n次 | o{5,10} |
sed
多重替换:
sed 's/[旧字符串]/[新字符串] ; s/[旧字符串]/[新字符串] ; ...' [文件名]
脚本替换:
sed -f [脚本名称] [文件名]
sed --file [脚本名称] [文件名]
sed -f [脚本名称] [文件名] > [新文件名] 保存输出
sed -n 's/[旧字符串]/[新字符串]' [文件名] 阻止输入行自动显示
删除:
sed '[数字]d' [文件名] 删除文件的第[数字]行
sed '[数字],[数字]d' [文件名] 删除文件[数字],[数字]行
sed '[数字],$d' [文件名] 删除文件[数字]到最后一行
sed '/[匹配字符]/d' [文件名] 删除匹配到某个字符的那一行
sed '/[匹配字符]/,[数字]d' [文件名] 匹配到这个字符,删除本行以及从本行开始到第[数字]行
sed '1~2d' [文件名] 删除奇数行
sed '0~2d' [文件名] 删除偶数行
sed '/^$/d' [文件名] 删除文件的空行
修改后替换源文件
sed -i 's/[旧字符串]/[新字符串] ; s/[旧字符串]/[新字符串] ; ...' [文件名]
sed -i.bak 's/[旧字符串]/[新字符串] ; s/[旧字符串]/[新字符串] ; ...' [文件名] 源文件备份
tips:
参数:
-f 指定脚本去替换
-n 阻止输入行自动显示
-i 替换原文件(没有此选项不会真正替换)
-i.[字符串] 将源文件进行备份以.[字符串]结尾
匹配最后所用参数:(与s//[参数]使用)
g 匹配到所有的字符被替换
[n]g 匹配到n次以后得字符被替换
d 删除
gi 忽略大小写替换
sed 字符替换时可以使用$变量
分隔符可以进行替换
awk
基本语法
BEGIN{} {} END{}
行处理前 行处理 行处理后
awk 'BEGIN{} {} END{}' [文件名]
awk的工作原理
awk -F":" '{print $1,$3}' /etc/passwd
(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束
(2)然后,行被:分解成字段,每个字段存储在已编号的变量中,从$1开始
(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab
(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为$1,$3间有一个,逗号。逗号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格
(5)awk打印字段时,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。
awk中的符号
FS 输入字段分隔符(默认空格)
OFS 输出字段分隔符 (默认空格)
NF 字段个数
NR 行数,记录编号
FNR 按不同的文件分开,记录编号
RS 输入记录分隔符(匹配到的字段进行回车换行)
ORS 输出记录分隔符(匹配到RS的地方进行ORS分隔)
awk使用
FS OFS:
awk 'BEAGIN{FS="[分隔符]" ; OFS="[输出的分隔符]"} {print $[列1],...} ' [文件名]
NR FNR:
awk -F: '{print NR,$0}' [文件名] 显示当前输出的行号
awk -F: '{print FNR,$0}' [文件名] 显示当前输出的行号(不同文件分开)
RS ORS:
awk -F: 'BEGIN{RS='[字段分隔符]'} {print $[列]}' [文件名] 输入记录分隔符
awk -F:'BEGIN{OFS='[输出字段分隔符]'} {print $[列]}' [文件名] 输出记录分隔符
传递变量:
echo | awk -v [变量名]=$[值] '{print [变量名]}'
指定某行某列:
awk -F[分隔符] 'NR==[行]{print $[列]}' [文件名]
expect
yum -y install expect
用法
在shell脚本中定义
定义命令:
1.set timeout 30
设置超时时间30s
2.spawn
spawn是进入expect环境后才能执行的内部命令,不能直接在默认的shell环境中执行。
传递交互命令
3.expect
判断输出结果是否包含某项字符串,没有则立即返回,否则等待一段时间后返回
等待通过timeout设置
4.send
执行交互动作,将交互要执行的动作进行输入给交互指令
命令字符串结尾要加上“r”,如果出现异常等待状态可以进行核查
5.interract
执行完后保持交互状态,把控制权交给控制台
如果不加这一项,交互完成会自动退出
6.exp_continue
继续执行接下来的操作
\r 回车
[ lindex $argv 0 ] 参数位置
语法:
1.设置变量和使用变量名
set [变量名] [值]
$[变量名]
2.使用交互命令
spawn 命令
3.匹配交互中的字符
expect{
"[匹配的字符]" {send '文本'}
}
4.交互
interact