0%

Shell脚本


Shell能做什么

  1. 自动化批量系统初始化程序
  2. 自动化批量软件部署程序
  3. 应用管理程序
  4. 日志分析处理程序
  5. 自动化备份恢复程序
  6. 自动化管理程序
  7. 自动化信息采集及监控程序
  8. 配合Zabbix信息采集
  9. 自动化扩容

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

1
su - 用户名
系统级: /etc/profile /etc/bashrc

nologin shell

1
su 用户名
用户级: ~/.bash_profile ~/.bashrc 离开shell时执行的文件 ~/.bash logout ~/.bash_history

命令历史记忆功能

上下键上一次/下一次执行过的命令
!数字执行指定序号的历史命令
!命令名找到相关命令名的命令执行
!$上一次命令执行过的最后一个参数
!!执行上一次执行后的命令

快捷键

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
2
3
4
5
cat <<-标记名 # - 表示:结束标记名前可跟缩进
输出内容1...
输出内容2...
输出内容3...
标记名

调用Python和Expect

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
echo "在shell脚本中调用Python或Expect"
/usr/bin/python <<-EOF
print "hellow word!"
EOF

注:给test后面的EOF加""即为把内容全部转义
cat > test <<-EOF
格化输出到test文件中...
EOF

变量类型

提升变量为全局变量:
 export 变量名 ...
 export 变量名=变量值 
自定义变量由用户自己定义、修改和使用
环境变量由系统维护, 用于设置工作环境.
位置变量通过命令行给脚本程序传递参数
预定义变量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
2
3
4
[function] 函数名(){
命令序列
[return x]
}

函数里的位置变量为调用函数时后面的值

函数调用

1
函数名 [值1] [值2]...

grep与egrep

文本处理工具基础正则表达式扩展正则表达式(限定符)
vi编辑器支持
grep支持
egrep支持支持
sed支持
awk支持支持

Sed与Awk

Sed工具概述

Sed是文本处理工具, 依赖于正则表达式, 可以读取文本内容, 根据指定条件对数据进行添加、删除、替换等操作.

Sed在处理数据时默认不直接修改源文件, 而是把当前处理的行存储在临时缓冲区中, 所有指令都在缓冲区中操作, 处理完成后,把缓冲区内容默认输出到屏幕接着处理下一行内容, 这样不断重复, 直到文件末尾, 文件本身内容没有改变.

Sed基本语法

sed [选项] '编辑指令' 文件1 文件2...
常用选项
-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行之后插入ADMIN
sed '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/nlogin
sed '/root/c admin:x:490:490::/:/sbin/nologin' /etc/passwd

把每行的第2个root替换成admin
sed -n 's/root/admin/2p' /etc/passwd

将包含root的所有行中的root都替换为ROOT
sed '/root/s/root/ROOT/g' /etc/passwd

将第1~3行中的所有bin都替换为BIN
sed '1,3s/bin/BIN/g' /etc/passwd/

在每行行尾插入字符串ABC
sed 's/$/ABC/' /etc/passwd

在每行行首插入#号
sed 's/^/#/' /etc/passwd

将包含root的行的行首插入#号
sed '/root/s/^/#/' /etc/passwd

将第一行替换为ABC
sed '1cABC' /etc/passwd

将root对应替换为ROOT, y表示对应替换.
sed 'y/root/ROOT/' /etc/passwd

将第1~10行中的root对应替换为ROOT
sed '1,10y/root/ROOT/' /etc/passwd

迁移符号条件的文本

H表示保存当前模式到一个缓冲区, G表示取出保存的模式.

将包含root的行另存为文件filel
sed '/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>

输出所有奇数行的内容
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}'

-------------------本文结束 感谢阅读-------------------