36.Linux Shell编程
文章目录
- Linux Shell编程
- 1. Linux 正则表达式
- 简介
- 分类
- 环境准备
- 普通字符
- 字符集
- 元字符
- 定位符
- 字符类
- 非打印字符
- 反向引用
- 综合案例
- Shell 三剑客
- 2. grep 使用手册
- 简介
- 语法
- 常用选项
- 模式选择
- 输出控制
- 文件查找
- 上下文控制
- 3. sed 使用手册
- 简介
- 工作流程
- 语法
- 示例1
- 示例2
- 常用选项
- 常用命令
- 寻址方式
- 4. awk 使用手册
- 简介
- 语法
- 脚本结构
- 示例
- 内置变量
- 选项
- 总结
Linux Shell编程
1. Linux 正则表达式
简介
- 正则表达式是一个
pattern
,用于匹配字符串。 - 由普通字符和元字符组成。
- 支持工具:
vim
、grep
、less
、sed
、awk
等。
分类
- 普通正则表达式
- 扩展正则表达式(支持更多元字符)
环境准备
[root@server ~ 19:02:20]# vim words
cat
category
acat
concatenate
dog
普通字符
[root@server ~ 19:02:45]# cat words | grep 'cat'
匹配时建议加上单引号,防止shell过度解释
示例
[root@server ~ 19:03:04]# touch c\*t [root@server ~ 19:07:32]# touch cbt [root@server ~ 19:07:38]# ls anaconda-ks.cfg cbt c*t words [root@server ~ 19:07:39]# cat words | grep c*t [root@server ~ 19:08:14]#
可以看见,我们什么结果都匹配不到
[root@server ~ 19:08:14]# cat words | grep 'c*t'
原因就是匹配c*t的时候,shell本身会先解释通配符,将其解释为cbt,我们在words中只有cat,所以在grep过滤的时候找不到相匹配的单词
字符集
-
[abc]
:匹配 a、b、c 中的任意一个[root@server ~ 19:09:26]# echo c1t >> words [root@server ~ 19:14:05]# echo cbt >> words [root@server ~ 19:14:08]# cat words | grep 'c[ab]t'
-
[a-z]
:匹配所有小写字母 -
[A-Z]
:匹配所有大写字母 -
[0-9]
:匹配所有数字[root@server ~ 19:21:02]# echo cAt >> words [root@server ~ 19:21:37]# echo czt >> words [root@server ~ 19:21:44]# cat words | grep 'c[a-z]t' [root@server ~ 19:21:48]# cat words | grep 'c[A-Z]t' [root@server ~ 19:22:44]# cat words | grep 'c[0-9]t'
补充:可以从小写匹配到大写,但是不能从大写匹配到小写
-
[^abc]
:匹配除了 a、b、c 以外的字符[root@server ~ 19:22:50]# cat words | grep 'c[^a-z]t' [root@server ~ 19:23:38]# cat words | grep 'c[^A-Z]t' [root@server ~ 19:23:45]# cat words | grep 'c[^0-9]t'
元字符
-
.
:匹配除换行符外的任意字符[root@server ~ 19:17:55]# echo cCt >> words [root@server ~ 19:19:21]# echo c-t >> words [root@server ~ 19:19:29]# cat words | grep 'c.t'
-
\
:转义字符[root@server ~ 19:25:47]# echo c.t >> words [root@server ~ 19:28:17]# cat words | grep 'c\.t'
-
|
:或(扩展正则)若要匹配|
,请使用\|
grep过滤的时候使用
egrep
或者grep -E
[root@server ~ 19:28:31]# cat words | egrep 'cat|dog' [root@server ~ 19:30:43]# cat words | grep -E 'cat|dog'
-
*
:匹配前一个字符0次或多次[root@server ~ 19:45:32]# echo dg >> words [root@server ~ 19:47:08]# echo dog >> words [root@server ~ 19:47:14]# echo doog >> words [root@server ~ 19:47:19]# cat words | grep 'do*g'
-
+
:匹配前一个字符1次或多次[root@server ~ 19:52:07]# cat words | egrep 'do+g'
-
?
:匹配前一个字符0次或1次[root@server ~ 19:53:19]# cat words | egrep 'do?g'
-
{n}
:匹配前一个字符n次[root@server ~ 19:55:13]# echo dooog >> words [root@server ~ 19:56:03]# echo doooog >> words [root@server ~ 19:56:06]# echo dooooog >> words [root@server ~ 19:56:10]# cat words | egrep 'do{3}g'
-
{n,}
:匹配前一个字符至少n次[root@server ~ 19:56:15]# cat words | egrep 'do{4,}g'
-
{n,m}
:匹配前一个字符n到m次[root@server ~ 19:56:52]# cat words | egrep 'do{2,5}g'
-
()
:子表达式[root@server ~ 19:47:37]# echo dog >> words [root@server ~ 19:50:14]# echo dogdog >> words [root@server ~ 19:50:20]# echo dogdogdog >> words [root@server ~ 19:50:24]# echo dogdogdogdog >> words [root@server ~ 19:50:29]# cat words | egrep '(dog){2,3}'
[root@server ~ 19:50:53]# cat words | egrep 'dog(dog)*'
定位符
-
^
:匹配行首[root@server ~ 19:40:04]# cat words | grep '^cat'
-
$
:匹配行尾[root@server ~ 19:40:56]# cat words | grep 'cat$' [root@server ~ 19:41:14]# cat words | grep '^cat$'
-
\b
:匹配单词边界[root@server ~ 19:39:24]# echo hello cat >> words [root@server ~ 19:39:39]# cat words | grep '\bcat' [root@server ~ 19:39:41]# cat words | grep 'cat\b' [root@server ~ 19:39:57]# cat words | grep '\bcat\b'
-
\B
:非单词边界[root@server ~ 19:41:28]# cat words | grep 'cat\B' [root@server ~ 19:45:14]# cat words | grep '\Bcat\B'
-
\<
:单词左边界 -
\>
:单词右边界
字符类
[[:digit:]]
:数字[[:alpha:]]
:字母[[:alnum:]]
:字母数字[[:space:]]
:空白字符[[:punct:]]
:标点符号
非打印字符
\n
:换行\t
:制表符\r
:回车\s
:空白字符\S
:非空白字符
反向引用
- 使用
(pattern)
捕获子表达式 - 使用
\1
、\2
等引用
综合案例
如何过滤出以下文件中所有有效的IPv4地址
0.0.0.0
1.1.1.1
11.11.11.111
111.111.111.111
999.9.9.9
01.1.1.1
10.0.0.0
0.1.1.1
266.1.1.1
248.1.1.1
256.1.1.1
一位数 [0-9]
两位数 [1-9][0-9]
三位数 [1-9][0-9][0-9]
0-255 [0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]
合并一位数到二位数 [1-9]?[0-9]
合并三位数 1[0-9]{2}|2[0-4][0-9]|25[0-5]
组合成IP地址 ([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])
答案:
\b(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b
[root@server ~ 20:00:25]# vim IPv4.txt
0.0.0.0
1.1.1.1
11.11.11.111
111.111.111.111
999.9.9.9
01.1.1.1
10.0.0.0
0.1.1.1
266.1.1.1
248.1.1.1
256.1.1.1
[root@server ~ 20:20:11]# cat IPv4.txt | egrep '\b(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b'
Shell 三剑客
2. grep 使用手册
简介
grep
是 Linux 文本处理三剑客之一,擅长过滤。
语法
grep [OPTION]... PATTERNS [FILE]...
command | grep [OPTION]... PATTERNS
常用选项
模式选择
-
-E
:扩展正则 -
-e
:多个模式cat words | grep -e 'dog' -e 'cat' 或者 cat words | egrep 'dog|cat'
-
-i
:忽略大小写 -
-w
:匹配整个单词cat words | grep -w 'cat' 或者 cat words | egrep '\bcat\b'
-
-x
:匹配整行cat words | grep -x 'cat' 或者 cat words | egrep '^cat$'
-
-v
:反向匹配,匹配与pattens
不匹配的项目[root@server ~ 20:33:02]# cat words | egrep -v '^d|^c'
-
-f
:从文件读取多个pattens
[root@server ~ 20:22:07]# echo -e 'cat\ndog' > pattens_file [root@server ~ 20:29:49]# cat pattens_file cat dog [root@server ~ 20:30:12]# cat words | grep -f pattens_file
输出控制
-
-m NUM
:最多匹配 NUM 行[root@server ~ 20:33:34]# cat words | grep -m2 'dog' dog dog
-
-c
:显示匹配行数[root@server ~ 22:32:23]# cat words | grep -c 'dog' 6
-
-n
:显示行号[root@server ~ 22:32:47]# cat words | grep -n 'dog' 5:dog 16:dog 18:dog 19:dogdog 20:dogdogdog 21:dogdogdogdog
-
-o
:只显示匹配部分[root@server ~ 22:33:11]# cat words | grep -o 'dog'
-
-q
:静默模式,不显示任何正常输入,通过特殊变量$?
查看匹配到的内容# 找不到的情况 [root@server ~ 22:34:04]# cat words | grep -q '(dog){3}' [root@server ~ 22:37:56]# echo $? 1# 找到的情况 [root@server ~ 22:38:16]# cat words | grep -q 'dog' [root@server ~ 22:39:05]# echo $? 0
-
-s
:不显示错误信息
文件查找
-
-r
:递归目录 -
-R
:递归并跟随软链接[root@server ~ 22:41:17]# grep -R enforcing /etc/ /etc/sysconfig/selinux:# enforcing - SELinux security policy is enforced. /etc/sysconfig/selinux:# permissive - SELinux prints warnings instead of enforcing. /etc/selinux/config:# enforcing - SELinux security policy is enforced. /etc/selinux/config:# permissive - SELinux prints warnings instead of enforcing. /etc/security/limits.conf:# - "soft" for enforcing the soft limits /etc/security/limits.conf:# - "hard" for enforcing hard limits
-
-l
:只显示文件名[root@server ~ 22:44:05]# grep -Rl enforcing /etc/ /etc/sysconfig/selinux /etc/selinux/config /etc/security/limits.conf
-
-L
:显示不匹配的文件名
上下文控制
-
-A NUM
:显示后 NUM 行 -
-B NUM
:显示前 NUM 行 -
-C NUM
:显示前后 NUM 行[root@server ~ 22:47:18]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:65:f5:8c brd ff:ff:ff:ff:ff:ffinet 10.1.8.10/24 brd 10.1.8.255 scope global noprefixroute ens33valid_lft forever preferred_lft foreverinet6 fe80::20c:29ff:fe65:f58c/64 scope link valid_lft forever preferred_lft forever[root@server ~ 22:47:59]# ip a | grep -B1 '00:0c:29:65:f5:8c' 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:65:f5:8c brd ff:ff:ff:ff:ff:ff[root@server ~ 22:48:03]# ip a | grep -A1 '00:0c:29:65:f5:8c'link/ether 00:0c:29:65:f5:8c brd ff:ff:ff:ff:ff:ffinet 10.1.8.10/24 brd 10.1.8.255 scope global noprefixroute ens33[root@server ~ 22:49:15]# ip a | grep -C1 '00:0c:29:65:f5:8c' 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:0c:29:65:f5:8c brd ff:ff:ff:ff:ff:ffinet 10.1.8.10/24 brd 10.1.8.255 scope global noprefixroute ens33
3. sed 使用手册
简介
sed
是流编辑器,非交互式处理文本,擅长替换。
工作流程
- 读取一行到模式空间(pattern space)
- 执行 sed 命令
- 输出并清空模式空间
- 重复直到文件结束
语法
sed [option] [sed-command] [input-file]
示例1
模拟cat命令打印文件内容
[root@server ~ 16:12:34]# cat > data.txt << 'EOF'
> I am studing sed
> I am www.hxl.cloud
> I am a Superman
> I am so handsome
> EOF[root@server ~ 16:13:37]# sed '' data.txt
I am studing sed
I am www.hxl.cloud
I am a Superman
I am so handsome
- 这里没有使用选项
''
,对应着[sed-command]
为具体的sed语句data.txt
,对应着[input-file]
,用于提供输入数据
示例2
从标准输入中读取数据
如果我们没有提供输入文件,那么sed默认会从标准输入中读取数据
[root@server ~ 16:14:10]# sed ''
# 输入hello world,并回车
hello world
# 输出hello world
hello world
# 按ctrl+d退出
常用选项
-
-e
:执行多个命令[root@server ~ 16:17:56]# sed -e '' data.txt I am studing sed I am www.hxl.cloud I am a Superman I am so handsome # 如果只有一个命令,一般`-e`选项省略# -e 可以多次使用,1d 作用是删除第一行 [root@server ~ 16:39:32]# sed -e '1d' -e '2d' -e '5d' data.txt I am a Superman I am so handsome # 因为第五行不存在,所以也就没有删除效果# 使用分号分开多个命令 [root@server ~ 16:40:13]# sed -e '1d;2d;5d' data.txt I am a Superman I am so handsome
-
-n
:取消自动打印,需要明确指明打印才会输出记录[root@server ~ 16:42:10]# cat data.txt | sed -e '1p' -e '3p' I am studing sed I am studing sed I am www.hxl.cloud I am a Superman I am a Superman I am so handsome # 仅打印当前的内容 [root@server ~ 16:47:06]# cat data.txt | sed -n -e '1p' -e '3p' I am studing sed I am a Superman
-
-f
:从文件读取命令(类比grep -f 的用法)[root@server ~ 16:49:26]# echo -e "1d\n2d\n5d" > scripts [root@server ~ 16:49:56]# cat scripts 1d 2d 5d [root@server ~ 16:50:00]# sed -f scripts data.txt I am a Superman I am so handsome
-
-i
:直接修改文件 -
-r
:使用扩展正则
常用命令
准备
[root@server ~ 16:55:41]# cat > test << 'EOF'
> This is 1
> This is 2
> This is 3
> This is 4
> This is 5
> EOF
-
p
:打印# 打印第3行 [root@server ~ 16:57:01]# cat test | sed -n '3p' This is 3# 打印1-3行 [root@server ~ 16:57:16]# cat test | sed -n '1,3 p' This is 1 This is 2 This is 3# `$p`表示最后一行# 打印第1行以及后续2行 [root@server ~ 16:57:35]# cat test | sed -n '1,+2 p' This is 1 This is 2 This is 3# 打印第1行以及后续隔2行输出 [root@server ~ 16:59:14]# cat test | sed -n '1~2 p' This is 1 This is 3 This is 5
准备
[root@server ~ 17:15:08]# cat > test << 'EOF'
>root:$6$jxEYiZ7ue7Jqt.ji$A801WVys57YeoIdX0eqn5RbkTXLlvkYUlui2k8MmmiZ.7.2eaj3NJrxxKZYyXZTpMCp33jJ/FJmhb4KaCaGDB.::0:99999:7:::
> bin:*:18353:0:99999:7:::
> daemon:*:18353:0:99999:7:::
> adm:*:18353:0:99999:7:::
> lp:*:18353:0:99999:7:::
> sync:*:18353:0:99999:7:::
> shutdown:*:18353:0:99999:7:::
> halt:*:18353:0:99999:7:::
> mail:*:18353:0:99999:7:::
> operator:*:18353:0:99999:7:::
> EOF
-
d
:删除 -
s
:替换[root@server ~ 17:17:59]# grep '^root' test root:$6$jxEYiZ7ue7Jqt.ji$A801WVys57YeoIdX0eqn5RbkTXLlvkYUlui2k8MmmiZ.7.2eaj3NJrxxKZYyXZTpMCp33jJ/FJmhb4KaCaGDB.::0:99999:7::: [root@server ~ 17:18:09]# grep '^root' test | sed 's/root/tankzhang/' tankzhang:$6$jxEYiZ7ue7Jqt.ji$A801WVys57YeoIdX0eqn5RbkTXLlvkYUlui2k8MmmiZ.7.2eaj3NJrxxKZYyXZTpMCp33jJ/FJmhb4KaCaGDB.::0:99999:7:::
补充:替换 ‘s/…/…/g’ g表示替换所有,不加g表示只替换匹配到的第一个
例如:关闭SElinux
sed -i 's/^SELINUX=.*/SELUNIX=disabled/g' config # -i 直接修改文件 # config 配置文件,这里没有写全
若查找替换时分隔符可以自定义,默认是/,如要替换/bin/bash 之类形式的文本,可以使用 s#/bin/bash#/bin/barsh#g,这样避免在使用时需要输入过多的转义字符
-
a
:在行后插入[root@server ~ 17:34:56]# cat test | sed '/^root/ahello world' root:$6$jxEYiZ7ue7Jqt.ji$A801WVys57YeoIdX0eqn5RbkTXLlvkYUlui2k8MmmiZ.7.2eaj3NJrxxKZYyXZTpMCp33jJ/FJmhb4KaCaGDB.::0:99999:7::: hello world bin:*:18353:0:99999:7::: daemon:*:18353:0:99999:7::: adm:*:18353:0:99999:7::: lp:*:18353:0:99999:7::: sync:*:18353:0:99999:7::: shutdown:*:18353:0:99999:7::: halt:*:18353:0:99999:7::: mail:*:18353:0:99999:7::: operator:*:18353:0:99999:7:::
-
i
:在行前插入[root@server ~ 17:18:55]# cat test | sed '/^root/ihello world' hello world root:$6$jxEYiZ7ue7Jqt.ji$A801WVys57YeoIdX0eqn5RbkTXLlvkYUlui2k8MmmiZ.7.2eaj3NJrxxKZYyXZTpMCp33jJ/FJmhb4KaCaGDB.::0:99999:7::: bin:*:18353:0:99999:7::: daemon:*:18353:0:99999:7::: adm:*:18353:0:99999:7::: lp:*:18353:0:99999:7::: sync:*:18353:0:99999:7::: shutdown:*:18353:0:99999:7::: halt:*:18353:0:99999:7::: mail:*:18353:0:99999:7::: operator:*:18353:0:99999:7:::
-
行尾插入:使用反向引用
[root@server ~ 17:43:09]# cat test | sed -r 's/^bin(.*)/bin\1end line/' bin:*:18353:0:99999:7:::end line
寻址方式
- 行号:
1,3p
- 模式:
/pattern/p
- 组合:
/start/,/end/p
4. awk 使用手册
简介
awk
是一门文本处理语言,擅长格式化输出和列处理。
语法
awk [options] 'script' file(s)
awk [options] -f scriptfile file(s)
脚本结构
awk
BEGIN { action }
pattern { action }
END { action }
示例
雇员信息表如下:
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
问题:想要的结果为
序号 姓名 部门 年龄
---------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
---------------
解答:
[root@server ~ 19:02:30]# cat > employee.txt << 'EOF'
> 1) 张三 技术部 23
> 2) 李四 人力部 22
> 3) 王五 行政部 23
> 4) 赵六 技术部 24
> 5) 朱七 客服部 23
> EOF
[root@server ~ 19:18:11]# cat employee.txt | awk '
> BEGIN {print "序号\t名字\t部门\t年龄\n---------------------\n"}
> {print}
> END {print "---------------------\n"}'
序号 名字 部门 年龄
---------------------1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
---------------------
内置变量
-
$0
:整行 -
$1, $2, ...
:第1列、第2列…[root@server ~ 17:43:21]# ip -br link lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> ens33 UP 00:0c:29:65:f5:8c <BROADCAST,MULTICAST,UP,LOWER_UP> [root@server ~ 18:58:03]# ip -br link | awk '/ens33/' ens33 UP 00:0c:29:65:f5:8c <BROADCAST,MULTICAST,UP,LOWER_UP> [root@server ~ 18:58:10]# ip -br link | awk '/ens33/ {print $3}' 00:0c:29:65:f5:8c[root@server ~ 18:58:26]# df 文件系统 1K-块 已用 可用 已用% 挂载点 devtmpfs 2001020 0 2001020 0% /dev tmpfs 2013060 0 2013060 0% /dev/shm tmpfs 2013060 11864 2001196 1% /run tmpfs 2013060 0 2013060 0% /sys/fs/cgroup /dev/mapper/centos-root 52403200 1933332 50469868 4% / /dev/sda1 1038336 142208 896128 14% /boot /dev/mapper/centos-home 49250820 33008 49217812 1% /home tmpfs 402616 0 402616 0% /run/user/0 [root@server ~ 19:01:52]# df | sed 's/%//g' 文件系统 1K-块 已用 可用 已用 挂载点 devtmpfs 2001020 0 2001020 0 /dev tmpfs 2013060 0 2013060 0 /dev/shm tmpfs 2013060 11864 2001196 1 /run tmpfs 2013060 0 2013060 0 /sys/fs/cgroup /dev/mapper/centos-root 52403200 1933332 50469868 4 / /dev/sda1 1038336 142208 896128 14 /boot /dev/mapper/centos-home 49250820 33008 49217812 1 /home tmpfs 402616 0 402616 0 /run/user/0 [root@server ~ 19:02:09]# df | sed 's/%//g' | awk '$5>10' 文件系统 1K-块 已用 可用 已用 挂载点 /dev/sda1 1038336 142208 896128 14 /boot
-
NF
:字段数[root@server ~ 19:20:17]# awk '{print $NF}' employee.txt 23 22 23 24 23
-
NR
:当前行号[root@server ~ 19:22:44]# awk 'NR==2 {print $NF}' employee.txt 22
选项
-F
(字段分隔符)
可以自定义分隔符,默认是空白符和逗号,
# 使用冒号作为分隔符
[root@server ~ 16:47:11]# echo "root:x:0:0:root:/root:/bin/bash" | awk -F: '{print $1}'
root# 使用多个分隔符
[root@server ~ 16:47:17]# echo "apple,orange;banana" | awk -F'[,;]' '{print $2}'
orange
-f
(从文件读取脚本)
类比grep和sed的-f 用法
[root@server ~ 16:47:29]# echo '{print $1, $3}' > script.awk
[root@server ~ 16:49:14]# awk -f script.awk data.txt
I studing
I www.hxl.cloud
I a
I so
-v
(定义变量)
在awk程序外部定义变量
# 定义变量
[root@server ~ 16:49:19]# awk -v name="John" -v age=30 'BEGIN{print "Name:", name, "Age:", age}'
Name: John Age: 30# 从shell变量传递值
[root@server ~ 16:50:58]# my_var="hello"
[root@server ~ 16:51:23]# awk -v var="$my_var" 'BEGIN{print var}'
hello
总结
实践是最好的老师!多写、多练、多思考,你很快就会成为 Shell 脚本高手!