Shell能做什么
- 自动化批量系统初始化程序
- 自动化批量软件部署程序
- 应用管理程序
- 日志分析处理程序
- 自动化备份恢复程序
- 自动化管理程序
- 自动化信息采集及监控程序
- 配合Zabbix信息采集
- 自动化扩容
…
Shell脚本的编写
注: 脚本文件名建议以.sh结尾
基本结构
脚本声明(即使用指定解释器解释):#!/bin/bash
脚本注释:#对该脚本的描述
脚本命令:命令序列
Shell脚本的运行
执行脚本文件
赋予脚本文件执行权限后, 程序会运行在一个全新的shell中, 不继承当前shell的环境变量的值, 同时若在程序中改变了当前shell中的环境变量(不使用export), 则当前shell的环境变量值不变.
chmod u+x 脚本文件 ./脚本文件 或 sh 脚本文件
bash [选项] 脚本名 |__ -n --检测脚本是否正确, 并不执行脚本. |__ -x --执行脚本, 输出执行过程. |__ -c --bash从字符串中读入命令, 如果字符串后还有变量就被设定为从$0开始的位置参数.
不赋予脚本文件执行权限, 只是暂时提升脚本文件执行权限, 程序继承当前shell中的环境变量, 同时, 若在程序中改变了当前shell中的环境变量(不使用export), 则当前shell中该环境变量的值也会改变.
. 脚本文件 或 source /脚本文件
shell脚本的调试
使用sh命令参数调试
sh [-nvx] 脚本名 |__ n --不会执行该脚本, 仅查询脚本语法是否有问题, 如果没有语法问题就不显示任何内容, 如果有问题就会提示报错. |__ v --在执行脚本时, 先将脚本的内容输出到屏幕上然后执行脚本, 如果有错误, 也会给出错误提示. |__ x --将执行的脚本内容输出到屏幕上, 这是个对调试很有用的参数.
shell的特性
命令和文件自动补齐
Tab键自动补齐
login shell和nologin shell
login shell登陆时执行的文件
/etc/profile /etc/bashrc ~/.bash_profile ~/.bashrc
nologin shell登陆时执行的文件
/etc/bashrc ~/.bashrc
login shell
系统级: /etc/profile /etc/bashrc
1 su - 用户名
nologin shell
用户级: ~/.bash_profile ~/.bashrc 离开shell时执行的文件 ~/.bash logout ~/.bash_history
1 su 用户名
命令历史记忆功能
上下键 | 上一次/下一次执行过的命令 |
!数字 | 执行指定序号的历史命令 |
!命令名 | 找到相关命令名的命令执行 |
!$ | 上一次命令执行过的最后一个参数 |
!! | 执行上一次执行后的命令 |
快捷键
Ctrl + R | 搜索历史命令 |
Ctrl + D | 退出 |
Ctrl + A | 将光标移动到最前面 |
Ctrl + E | 将光标移动到最后面 |
Ctrl + L | 清屏 |
Ctrl + U | 将光标处前面部分删除 |
Ctrl + K | 将光标后后面部分删除 |
Ctrl + Y | 撤销 |
Shift + V | 进入行选中模式 |
Shift + <或> | 减少缩进/增加缩进 |
命令排序
;
: 不具备逻辑判断&&
,||
: 具备逻辑判断
shell通配符
(命令)
: 在子shell中执行命令{项1,项2,项3,...}
: 项的集合
Bash中调用Python Expect
标记输出
1 | cat <<-标记名 # - 表示:结束标记名前可跟缩进 |
调用Python和Expect
1 |
|
变量类型
自定义变量 | 由用户自己定义、修改和使用 |
环境变量 | 由系统维护, 用于设置工作环境. |
位置变量 | 通过命令行给脚本程序传递参数 |
预定义变量 | Bash中内置的一类变量, 不能直接修改. |
/etc/profile
环境变量的全局配置文件(用户):
~/.bash_profile
使用
env
命令查看当前工作环境下的环境变量
变量操作
定义 | 变量名 |
赋值 | 变量名=值 |
使用 | $变量名 |
同时定、赋、使 | $((变量名=值)) |
区分 | ${变量名} |
read是用来读取用户输入信息的命令, 能够把接收到的用户输入信息赋值给后面的指定变量.
read [选项] 变量名1, 变量名2,... |__ p --用于向用户显示一定的提示信息 |__ n --后跟一个数字, 定义输入文本的长度. |__ t --后面跟秒数, 定义输入字符的等待时间.
# 查看变量的内容 echo ${变量名} # 查看该变量的长度 echo ${#变量名} # 截取从指定索引开始后的所有字符 echo ${变量名:字符索引} # 截取从指定索引开始后的指定数量的字符 echo ${变量名:字符索引:截取个数}
变量删除
# 删除变量里从左边开始匹配的字符串内容 echo ${变量名#字符串} # 删除第一次匹配的字符串所在处左边的内容 echo ${变量名#*字符串} # 删除最后一次匹配的字符串所在处左边的内容 echo ${变量名##*字符串} # 删除变量里从右边开始匹配的字符串内容 echo ${变量名%字符串} # 删除第一次匹配的字符串所在处右边的内容 echo ${变量名%字符串*} # 删除最后一次匹配的字符串所在处右边的内容 echo ${变量名%%字符串*}
变量替换
# 将源字符串替换成指定字符串 echo ${变量/源字符串/指定字符串} # 将源字符串替换成指定字符串(全部匹配) echo ${变量//源字符串/指定字符串}
变量替代
unset var1 # 输出aaaa echo ${var1-aaaa} var2= # 输出空值 echo ${var2-bbbb} ${变量名-新的变量值} |__ 变量没有被赋值: 会使用"新的变量值"替代 |__ 变量有被賦值(包括空值): 不会被替代 ${变量名:-新的变量值} |__ 变量没有被赋值(包括空值): 会使用"新的变量值"替代 |__ 变量有被賦值: 不会被替代 ${变量名+新的变量值} |__ 变量没有被赋值: 不会被替代 |__ 变量有被賦值(包括空值): 会使用"新的变量值"替代 ${变量名:+新的变量值} |__ 变量没有被赋值(包括空值): 不会被替代 |__ 变量有被賦值: 会使用"新的变量值"替代 ${变量名=新的变量值} |__ 变量没有被赋值: 原变量会被"新的变量值"赋值 |__ 变量有被賦值(包括空值): 原变量不会被赋值 ${变量名:=新的变量值} |__ 变量没有被赋值(包括空值): 原变量会被"新的变量值"赋值 |__ 变量有被賦值: 原变量不会被赋值 ${变量名?新的变量值} |__ 变量没有被赋值: 输出bash: 变量名: "新的变量值" |__ 变量有被賦值(包括空值): 输出原变量值 ${变量名:?新的变量值} |__ 变量没有被赋值(包括空值): 输出bash: 变量名: "新的变量值" |__ 变量有被賦值: 输出原变量值
整数运算
$RANDOM是存放一个随机数的变量, $()同``.
方式一
expr命令是求表达式变量的值, 一般用于整数值, 也可用于字符串.
expr $a + $b expr $a \* $b
方式二
** :表示求次方运算
$((整数表达式))
echo $(($a+$b)) echo $(($a*$b)) echo $(($a**$b))
方式三
$[整数表达式]
echo $[$a+$b] echo $[$a*$b] echo $[$a**$b]
方式四
let命令是用于执行一个或多个表达式, 变量计算中不需要加上$来表示变量.
let a=5+6 b=10*3 c=6-1,... let a=b+c,... echo $a $b $c
小数运算
echo "scale=保留位;1/3" | bc echo "print 5.0/2" | python awk 'BEGIN{print 1/3}' # 有效位为6位
各种符号
` | 将执行某个命令的结果赋值给变量 |
'(强引用) | 对所有字符进行转义 |
"(弱引用) | 对字符串进行边界划分 |
() | 在子shell中执行 |
(()) | 数值比较 |
$() | 命令替换 |
$(()) | 整数运算 |
{} | 集合 |
${} | 变量区分 |
[] | 条件测试 |
[[]] | 条件测试, 支持正则. |
$[] | 整数运算 |
接收用户参数
在执行脚本文件后直接加上参数, 即脚本文件 参数1 参数2 参数3,...
$0 | 脚本文件的名称 |
$n | 第n个用户参数 |
$# | 命令行中位置变量的个数 |
$* | 所有用户参数(所有参数为整体) |
$@ | 所有用户参数(每个参数为独立) |
$$ | 当前进程的PID |
$! | 上一个后台进程的PID |
判断用户参数
条件测试语句的执行格式:[ 条件表达式 ] / test 条件表达式
将[ 条件表达式 ]
作为一个条件测试语句:[[ 条件表达式 ]]
检查语句执行结果:echo $?
正确返回0, 错误返回非0.
状态码 | 描述 |
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的shell命令 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效的退出 |
128+x | 与Linux信号x相关的严重错误 |
130 | 通过ctrl+c终止的命令 |
255 | 正常范围之外的退出状态码 |
文件测试语句
-d | 测试文件是否为目录类型 |
-e | 测试文件是否存在 |
-L | 测试文件是否存在并且是一个符号链接 |
-f | 判断是否为一般文件 |
-r | 测试当前用户是否有权限读取 |
-w | 测试当前用户是否有权限写入 |
-x | 测试当前用户是否有权限执行 |
变量测试语句
-z | 判断变量的值的长度为0 |
-n | 判断变量的值的长度为非0 |
逻辑测试语句
&& | 前面的命令执行成功后才会执行它后面的命令 |
|| | 当前面的命令执行失败后才会执行它后面的命令 |
! | 把条件测试中的判断结果取相反值 |
整数值比较语句
-eq | 是否等于 |
-ne | 是否不等于 |
-gt | 是否大于 |
-lt | 是否小于 |
-le | 是否等于或小于 |
-ge | 是否大于或等于 |
字符串比较语句
=~ | 判断字符串和右边的正则表达式范例是否匹配 |
== | 比较字符串内容是否相同 |
!= | 比较字符串内容是否不同 |
-z | 判断字符串内容是否为空 |
流程控制语句
if条件测试语句
read -p "提示信息" 变量名
命令序列中只写:
表示返回真
单分支
1
2
3
4 if #条件测试操作
then
#命令序列
fi
双分支
1
2
3
4
5
6 if #条件测试操作
then
#命令序列1
else
#命令序列2
fi
多分支
1
2
3
4
5
6
7
8
9 if #条件测试操作1
then
#命令序列1
elif #条件测试操作2
then
#命令序列2
else
#命令序列3
fi
for条件循环语句
/dev/null是一个被称作Linux黑洞的文件, 把输出信息重定向到这个文件等同于删除数据(类似于没有回收功能的垃圾箱), 可以让用户的屏幕窗口保持简洁.
.. :表示范围(即 ?..?, 多少至多少.)
for循环
1
2
3
4 for 变量名 in #取值列表(每个值之间空格分隔)
do
#命令序列
done
while条件循环语句
exit命令使shell以指定的状态值退出, 若不设置状态值参数, 则shell以预设值退出.
例:exit 0
while循环
1
2
3
4 while #条件测试操作
do
#命令序列
done
case条件测试语句
case测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14 case 变量值 in
#模式1)
#命令序列1
;;
#模式2)
#命令序列2
;;
......
*)
#默认命令序列
esac
shell函数
定义格式
1 | [function] 函数名(){ |
函数里的位置变量为调用函数时后面的值
函数调用
1 | 函数名 [值1] [值2]... |
grep与egrep
文本处理工具 | 基础正则表达式 | 扩展正则表达式(限定符) |
vi编辑器 | 支持 | |
grep | 支持 | |
egrep | 支持 | 支持 |
sed | 支持 | |
awk | 支持 | 支持 |
Sed与Awk
Sed工具概述
Sed是文本处理工具, 依赖于正则表达式, 可以读取文本内容, 根据指定条件对数据进行添加、删除、替换等操作.
Sed在处理数据时默认不直接修改源文件, 而是把当前处理的行存储在临时缓冲区中, 所有指令都在缓冲区中操作, 处理完成后,把缓冲区内容默认输出到屏幕接着处理下一行内容, 这样不断重复, 直到文件末尾, 文件本身内容没有改变.
Sed基本语法
-e | 指定要执行的命令, 只有一个编辑命令时可省略. |
-n | 只输出处理后的行, 读入时不显示. |
-i | 直接编辑文件, 而不输出结果. |
[地址1] [地址2] 操作 [参数]
没有地址则代表所有的行, 参数g代表只要符合条件, 全部都进行处理.
p | 输出指定的行 |
d | 删除指定的行 |
s | 字串替换, 格式:"行范围s/旧字符串/新字符串/g" |
r | 读取指定文件 |
w | 保存为文件 |
a | 在当前行后面插入一行或多行 |
c | 用新文本替换定位文本 |
i | 在当前行前面插入一行或多行 |
n | 从文件中读取文本下一行, 并从下一条命令而不是第一条命令开始对其的处理. |
y | 传送字符, 替换单个字符. |
= | 显示文件行号 |
Sed用法示例
输出指定的行
将所有内容输出sed -n 'p' /etc/passwd
将第6行内容输出sed -n '6p' /etc/passwd
将第6~8行内容输出sed -n '6, 8p' /etc/passwd
将所有奇数行输出sed -n 'p;n' /etc/passwd
将所有偶数行输出sed -n 'n;p' /etc/passwd
将1~10行中的奇数行输出sed -n '1,10p;n' /etc/passwd
将1~10行中的偶数行输出sed -n '1,10{n;p}' /etc/passwd
将第10行到末尾之间的奇数行输出sed -n '10,${n;p}' /etc/passwd
将最后一行输出sed -n '$p' /etc/passwd
将第1行开始, 连续4行进行输出(1~5行).sed -n '1,+4p' /etc/passwd
将匹配包含root的行进行输出sed -n '/root/p' /etc/passwd
将从第10行至第一个包含nom之间的行进行输出sed -n '10,/nom/p' /etc/passwd
匹配不少于1次前导字符o, 加-r参数:支持拓展正则表达式.sed -nr '/ro{1,}t/p' /etc/passwd
输出包含root或者ntp的行sed -n '/root\|ntp/p' /etc/passwd
将包含nom所在的行行号输出, “=”号用来输出行号.sed -n '/nom/=' /etc/passwd
输出前5行信息后退出, q退出.sed -e '5q' /etc/passwd
插入符合条件的行
添加多行数据, 除最后一行外, 每行末尾都需要用”\n”符号表示数据未完结, 换行.
在含有root行的前面一行添加admin:x:490:490:/sbin nologin
sed '/root/i admin:x:490:490::/:/sbin/nologin' /etc/passwd
在含有root行的后面一行添加admin:x:490:490:/sbin nologin
sed '/root/a admin:x:490:490::/:/sbin/nologin' /etc/passwd
在第3行之后插入ADMINsed '3aADMIN' /etc/passwd
删除符合要求的行
删除第1行sed '1d' /etc/passwd
删除最后1行sed '$d' /etc/passwd
删除所有空行sed '/^$/d' /etc/passwd
删除第2-4行sed '2,4d' /etc/passwd
删除含有root的行sed '/root/d' /etc/passwd
删除不包含root的行, 这里的”!”号表示取反操作.sed '/root/!d' /etc/passwd
删除以root开头的行sed '/^root/d' /etc/passwd
删除以nologin结尾的行sed '/nologin$/d' /etc/passwd
替换符号条件的文本
将文件中所有的root都删除sed 's/root//g' /etc/passwd
将含有root的行替换为admin:x:490:490:/sbin/nloginsed '/root/c admin:x:490:490::/:/sbin/nologin' /etc/passwd
把每行的第2个root替换成adminsed -n 's/root/admin/2p' /etc/passwd
将包含root的所有行中的root都替换为ROOTsed '/root/s/root/ROOT/g' /etc/passwd
将第1~3行中的所有bin都替换为BINsed '1,3s/bin/BIN/g' /etc/passwd/
在每行行尾插入字符串ABCsed 's/$/ABC/' /etc/passwd
在每行行首插入#号sed 's/^/#/' /etc/passwd
将包含root的行的行首插入#号sed '/root/s/^/#/' /etc/passwd
将第一行替换为ABCsed '1cABC' /etc/passwd
将root对应替换为ROOT, y表示对应替换.sed 'y/root/ROOT/' /etc/passwd
将第1~10行中的root对应替换为ROOTsed '1,10y/root/ROOT/' /etc/passwd
迁移符号条件的文本
H表示保存当前模式到一个缓冲区, G表示取出保存的模式.
将包含root的行另存为文件filelsed '/root/w file1' /etc/passwd
将包含root的行迁移至末尾sed '/root/{H;d};$G' /etc/passwd
将第1~5行内容迁移至末尾sed '1,5{H;d};$G' /etc/passwd
将包含root的行迁移至末尾sed '/root/{H;d};$G' /etc/passwd
执行多次命令
-e可以将多个命令连接起来, 也可将多个编辑命令保存到文件中, 通过-f指定文件, 已完成多个处理操作.
将root和bash行作替换sed -ne 's/root/admin/' -ne 's/bash/sh/p' /etc/passwd
直接修改文件内容
在每行开头插入#号, 直接修改原文件.sed -i 's/^/#/' /etc/passwad
将每行开头的#号删除, 直接修改原文件.sed -i 's/^#//g' /etcpasswd
Awk编辑工具概述
Awk是一个功能强大的编辑工具, 用于在Linux/UNIX下对文本和数据进行处理.
数据可以来自一个或多个文件, 也可以为其他命令的输出, 常作为脚本来使用.
在执行操作时, Awk逐行读取文本, 默认以空格为分隔符进行分隔, 将分隔所得的各个字段保存到内建变量中, 对比该行是否与给定的模式相匹配, 并按模式或者条件执行编辑命令, 也可从脚本中调用编辑指令过滤输出相应内容.
Awk基本语法
Awk的两种语法格式awk [选项] '模式或条件{编辑指令}' 文件1 文件2
awk -f 脚本文件 文件1 文件2
模式可以为条件语句、复合语句或正则表达式等
每条编辑指令可以包含多条语句, 多条语句之间要使用分号或者空格分隔的多个1区域.
常用选项-F定义字段分隔符, 默认以空格或者制表符作为分隔符.
FS | 指定每行文本的字段分隔符, 缺省为空格或制表位. |
NF | 当前处理的行的字段个数 |
NR | 当前处理的行的行号(序数) |
$0 | 当前处理的行的整行内容 |
$n | 当前处理行的第n个字段(第n列) |
Awk用法示例
打印文本内容
输出以root开头的行awk '/^root/{print}' /etc/passwd
输出以nologin结尾的行awk '/nologin$/{print}' /etc/passwd
统计可登录系统用户的个数. 使用管道符调用命令wc-1统计使用bash的用户个数即可登录系统用户的个数.awk -F ':' '/bash$/{print|"wc -l"}' /etc/passwd
输出第1行到第3行内容awk 'NR==1,NR==3{print}' /etc/passwd
输出第1、3行内容awk 'NR==1||NR==3{print}' /etc/passwd
输出第1行到第3行内容awk '(NR>=1)&&(NR<=3){print}' etc passwd< code>=3){print}'>
输出所有奇数行的内容awk '(NR%2)==1{print}' /etc/passwd
输出所有偶数行的内容awk '(NR%2)==0{print}' /etc/passwd
输出第3个字段不小于900的行, “!”号表示取反.awk -F ':' '!($3 < 900)' /etc/passwd
输出第3个字段大于200的行awk -F ':' '{if($3>200){print $0}}' /etc/passwd
按字段输出文本
如果第3个字段的值大于第4个字段的值, 则把问号前表达式的值赋给max, 否则就将冒号后那个表达式的值赋给max.awk -F ':' '{max=($3>$4)?$3:$4;print max}' /etc/passwd
如果第3个字段的值大于200, 则把第3个字段的值赋给max, 否则就将第1个字段的值赋给max.awk -F ':' '{max=($3>200)?$3:$l;print max}' /etc/passwd
输出处理数据的行号, 每处理完一条记录, NR值加1.awk -F ':' '{print NR,$O}' /etc/passwd
输出第3列小于5的第1列与第3列数据awk -F ':' '$3<5{print $1 $3}' /etc/passwd
输出包含7个字段, 并且第1个字段中包含root的行的第1与第2个字段内容.awk -F ':' '($1~"root")&&(NF==7){print $1,$3}' /etc/passwd
输出第3行到第7行中以冒号为分隔符的第1列与第7列的数据awk -F ':' 'NR==3,NR==7{print $1,$7}' /etc/passwd
awk -F ':' 'NR==3,NR==7{print "USERNAME:" $1, "SHELL:" $7}' /etc/passwd
输出Hi,加以root开头的第1个字段.awk -F ':' '/^root/{print "Hi,"$1}' /etc/passwd
输出以冒号分隔且第7个字段中包含/bash的行的第1个字段awk -F ':' '$7~"/bash"{print $1}' /etc/passwd
保留原来的格式, 输出以冒号为分隔. /etc/passwd文件的前4个字段.awk -F ':' '{print $1":"$2":"$3":"$4}' /etc/passwd
输出以冒号为分隔符的第1列和第3列数据awk -F ':' '{print $1,$3}' /etc/passwd
在FS之前加一个BEGIN, 当读取第一条数据之前, 先把分隔符加上后再进去操作. 相似的还有END, 在所有数据处理完毕后执行.awk 'BEGIN{FS=":"}{print $1,$3}' /etc/passwd
统计以/bin/bash为结尾的行数awk 'BEGIN{X=0};/\/bin\/bash$/{x++};END{print x}' /etc/passwd
处理命令输出的结果
输出月份和年份date | awk '{print "Month:"$2 "\nYear:", $6}'