shell三剑客grepsedawk精讲
Posted 如何在5年薪百万
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell三剑客grepsedawk精讲相关的知识,希望对你有一定的参考价值。
总览
- grep 文本过滤器
- sed 流编辑器
- awk 报告生成器
grep
基本语法
- 以行为单位过滤
- 通过匹配规则,对每一行进行匹配查找进行filter操作,匹配上的输出改行,否则丢弃改行
## 方式1
grep [option][pattern][file1,file2,file3]
## 方式2(常用)
comand | grep pattern
选项 | 含义 |
---|---|
-v | 取反,不包含某一行信息(常用) |
-i | 忽略大小写 |
-n | 显示行号 |
-r | 递归搜索 |
-E | 支持正则匹配 |
-F | 不使用正则,按照字面匹配(刚好搜索内容包含特殊字符) |
输出文件case.sh中不包含if的行
[root@server1 shell]# grep -v if case.sh
#!/bin/bash
case $1 in
jack )
echo "hello jack"
;;
mike )
echo "hello mike"
;;
* )
echo "every one"
;;
esac
将文件中包含echo或者mike的行输出
竖杠是正则表达式,如果不使用-E,会当做一整个字符串匹配
[root@server1 shell]# grep -E "echo | mike" case.sh
echo "hello jack"
echo "hello mike"
echo "every one"
递归某个目录下所有文件,没有目录是默认当前目录及子目录
[root@server1 dir]# grep -r case ../
../dir/test.sh:case1
../case.sh:case $1 in
tip1 输出结果前后N行
生产查询log时,单纯查询error关键字看不到内容,通过下面方法可以输出前后n行,获取鞥多信息
件控制:
- B, --before-context=NUM 打印以文本起始的NUM 行
- A, --after-context=NUM 打印以文本结尾的NUM 行
[root@server1 log]# grep -A5 -B3 php yum.log-20200104
Oct 13 14:39:24 Installed: mailcap-2.1.41-2.el7.noarch
Oct 13 14:39:25 Installed: httpd-2.4.6-90.el7.centos.x86_64
Oct 13 14:39:31 Installed: libzip-0.10.1-8.el7.x86_64
Oct 13 14:39:31 Installed: php-common-5.4.16-46.el7.x86_64
Oct 13 14:39:32 Installed: php-cli-5.4.16-46.el7.x86_64
Oct 13 14:39:32 Installed: php-5.4.16-46.el7.x86_64
Nov 19 11:46:54 Updated: 7:device-mapper-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Updated: 7:device-mapper-libs-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Installed: 7:device-mapper-event-libs-1.02.158-2.el7_7.2.x86_64
Nov 19 11:46:54 Installed: libaio-0.3.109-13.el7.x86_64
Nov 19 11:46:54 Installed: device-mapper-persistent-data-0.8.5-1.el7.x86_64
sed
stream editor 流编辑器,对标准输出或者文件逐行进行处理工具。
总结:sed本质上是针对文本以行为单位进行增删改查的工具。
语法格式
# 格式1
stdout | sed [option] "pattern command"
# 格式2
sed [option] "patthern command" file
逐行匹配,匹配上的行执行命令。匹配不上的行不做处理直接跳过。
和grep处理过程类似,只是grep类似filter过滤器,而sed类似于map转换器
option讲解
选项 | 含义 |
---|---|
-n | 只打印匹配航 |
-e | 直接在命令行进行sed编辑 ,default |
-i | 直接修改源文件 |
-r | 支持正则标傲世 |
-f | 从指定文件读取编辑动作执行 |
-n 静默输出
# -n静默模式,只打印匹配到的行
[root@server1 shell]# sed '/python/p' sed_01.sh
#!/bin/bash
I love python
I love python
I love Python
注:sed默认打印原始行,通过-n可以只打印匹配到的行
/正则表达式/
[root@server1 shell]# sed -n '/python/p' sed_01.sh
I love python
-e 从编辑流获取执行
- 多个编辑命令需要-e串起来
[root@server1 shell]# sed -n -e '/python/p' -e '/Python/p' sed_01.sh
I love python
I love Python
- 可以读取管道符的结果,而不需要从文件读取
-f 读取文件执行
# 把命令放入文件
[root@server1 shell]# cat edit.sed
/python/p
# 执行时从文件读取执行命令
[root@server1 shell]# sed -n -f edit.sed sed_01.sh
I love python
-r 扩展正则表达式
没有-r则不支持正则表达式写法
[root@server1 shell]# sed -n -r '/python|Python/p' sed_01.sh
I love python
I love Python
-i 直接修改源文件
# 替换命令直接对源文件生效
[root@server1 shell]# sed -i 's/love/like/g' sed_01.sh
[root@server1 shell]# cat sed_01.sh
#!/bin/bash
I like python
I like Python
pattern详解
pattern即匹配规则,sed对于匹配上的行才会处理
匹配模式 | 含义 | 示例 |
---|---|---|
10command | 匹配第10行 | sed -n “17p” file 打印17行 |
10,20command | 10~20行 | sed -n “17,20p” file 打印17到20行 |
10,+5command | 10行~10+5行 | |
/pattern1/command | 匹配到pattern1的行 | sed -n “/^root/p” file 打印以root开头德夯 |
/pattern1/,/pattern2/command | 从匹配到pattern1开始到匹配到pattern2的行结束 | |
10,/pattern1/command | 从10行开始到匹配到pattern1结束 |
只有上述匹配模式,都好代表范围
示例
# 打印17行
[root@server1 shell]# sed -n '17p' /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
# 打印17到20行
[root@server1 shell]# sed -n '17,20p' /etc/passwd
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
# 打印以nginx开头的行
[root@server1 shell]# sed -n -r '/^nginx/p' /etc/passwd
nginx:x:997:993:Nginx web server:/var/lib/nginx:/sbin/nologin
# 以mail开头的行到以ftp开头的行
[root@server1 shell]# sed -n -r '/^mail/,/^ftp/p' /etc/passwd
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
编辑命令详解
命令 | 含义 |
---|---|
p | 打印 |
a | append 行后追加 |
i | insert 行前追加 |
r | read 外部文件读入,行后追加 |
w | write 匹配航写入外部文件 |
d | 删除 |
s/old/new | 替换第一个 |
s/old/new/g | 全部替换 |
s/old/new/ig | 全局替换忽略大小写 |
d 删除行
# 删除1到10行
sed -i '1,10d;p' passwd
# 删除所有不能登录用户
[root@server1 shell]# sed -i '/\\/sbin\\/nologin/d' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
追加行
-a 在匹配到的行后追加一行
[root@server1 shell]# sed -i '/\\/bin\\/bash/a this is user which can login' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
this is user which can login
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
-i 在匹配到的行前插入(append插入行后)
[root@server1 shell]# sed -i '/^mail/,/^ftp/i ----insert before line matched----' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
----insert before line matched----
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
----insert before line matched----
operator:x:11:0:operator:/root:/sbin/nologin
----insert before line matched----
games:x:12:100:games:/usr/games:/sbin/nologin
----insert before line matched----
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
-r 读取文件中的内容,append匹配到的行后
[root@server1 shell]# sed -i '/root/r list' passwd
[root@server1 shell]# cat passwd
root:x:0:0:root:/root:/bin/bash
first line ddddd
2nd line xxxxx
3rd line ------
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
first line ddddd
2nd line xxxxx
3rd line ------
-w 匹配到的行输出到文件
sed '/root/w output.txt' passwd
[root@server1 shell]# clear
[root@server1 shell]# cat output.txt
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
修改
替换所有的root为XXXX
[root@server1 shell]# sed -i 's/root/XXXX/g' passwd
[root@server1 shell]# cat passwd
XXXX:x:0:0:XXXX:/XXXX:/bin/bash
反向引用
引用匹配到的内容作为操作对象
# 给匹配到的内容加s
[root@server1 shell]# cat sed_02
HadAAP is good
HadBBc is not good
HadCCe is really shit
[root@server1 shell]# sed -i 's/Had.../&s/g' sed_02
[root@server1 shell]# cat sed_02
HadAAPs is good
HadBBcs is not good
HadCCes is really shit
可以使用\\1做部分引用替换,用到时在查询
查询精讲
使用shell脚本统计配置文件
#!/bin/bash
FILE_NAME=$1
function get_all_sections
echo ` sed -n '/\\[.*\\]/p' $FILE_NAME | sed -e 's/\\[//g' -e 's/\\]//g'`
function count_section
index=0
for i in `get_all_sections`
do
index=`expr $index + 1`
num=`sed -n '/\\['$i'\\]/,/\\[.*\\]/p' $FILE_NAME | grep -v '^#' | grep -v '^$' | grep -v '\\[.*\\]' | wc -l`
echo "$index : $i $num"
done
count_section
统计结果
[root@server1 shell]# sh parse_mycnf.sh my.cnf
1 : mysqld 7
2 : mysql-03 3
3 : mysql-01 8
4 : mysql-02 6
5 : sdf 0
如何排除掉空行
#排除掉结果中的空行
命令| grep -v '^$'
删除精讲
删除配置文件中所有空行和以#开头的注释行
sed -i '/[:blank:]*#/d;/^$/d' nginx.conf
分号可以隔开多个sed命令
给非注释行添加*
- 非#开头的行匹配规则
- 反向引用,如何添加字符
sed -i 's/^[^#]/\\*&/g' nginx.conf
修改精讲
- 掌握模式匹配控制修改的范围
- 全局修改与局部修改
# 1到10行中匹配并替换
1,10s/old/new
# 替换第一行中的root为Root
sed -i '1s/root/Root/g' passwd
# 替换5到10行中的的 /sbin/nologin为/bin/bash
sed -i '5,10s/\\/sbin\\/nologin/\\/bin\\/bash/g' passwd
# 匹配到 /sbin/nologin的行,并替换其中的login为LOGIN
sed -i '/\\/sbin\\/nologin/s/login/LOGIN/g' passwd
# 删除文本中所有的数字
sed -i 's/[0-9]*//g' nginx.conf
s前可以写sed的匹配规则,s后写替换规则
追加精讲
- a append匹配后追加
- i insert行前插入
- r 读取文件内容追加到侯敏
- w 匹配行写入文件
# 在第十行后面追加linebeadhn
sed -i '10a add linebeadhn' passwd
# 10行到20行前insert字符
sed -i '4,10i insert beform line' passwd
# 将/etc/fstab文件内容添加到20行后
sed -i '20r /etc/fstab' passwd
# 匹配到/bin/bash的行并追加文件内容
sed -i '/\\/bin\\/bash/r /etc/fstab' passwd
# 匹配包含内容的行并输出到 out.bash文件中
sed -i '/\\/sbin\\/nologin/w out.bash' passwd
AWK命令详解
awk理解为报告生成器,有专门讲awk编程的教材。
个人认为把一个命令做成编程的复杂度度,是不太合适的
语法格式
## 格式1
awk 'BEGINpatterncommandsEND' filename
## 格式2
管道 | awk命令
- BEGIN 前置命令,正式处理数据之前执行
- pattern 匹配模式
- commands 处理命令,可能多行
- END 处理完所有匹配数据后执行
学习完awk后对这个命令的设计感到震撼,有些人终其一生也无法写出类似的工具,像伟大程序员执行
awk命令本质上格式如下
awk '' filename
- 代表要执行的命令,多个可以串行的
- BEING是修饰,代表后面的命令需要在文件打开前执行
- END修饰的,代表里面的命令在文件关闭后执行
- 没有修饰符的代表,读取每一行是执行
- 之外的代表过滤器,符合匹配规则的行才会被处理
这个很接近于响应式编程,每一行文件数据像一个个小球被抛出,被awk逐个过滤和处理
awk内置变量
内置变量 | 含义 |
---|---|
$0 | 整行内容 |
$n | 分隔符后,代表第n个变量 |
NF | Number of Field当前行字段个数,也就是多少列 |
NR | Num of row 当前的行号,从1开始计数 |
FNR | 多文件处理时,每个文件行号单独计数,都是从0开始 |
FS | file separator,默认空格或者tab |
RS | row separator 输入行分隔符。默认回车换行 |
OFS | 输出字段分隔符。默认空格 |
ORS | 输出行分隔符,默认为回车换行 |
FILENAME | 文件名 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
# 打印passwd的第7列,分隔符为冒号
awk 'BEGINFS=":"print $7' passwd
# 默认分隔符获取年龄
[root@server1 shell]# cat awktest
zhangsan 12 shanghai
lisi 33 beijing
wangwu 33 chengdu
[root@server1 shell]# awk 'print $2' awktest
12
33
33
# NF、NR、FNR(FNR对多个文件能看到效果)
1
2
3
[root@server1 shell]# awk 'print FNR' awktest
1
2
3
[root@server1 shell]# awk 'print FNR' awktest passwd
# 指定row的分隔符为---而不是回车
[root@server1 shell]# awk 'BEGINRS="---"print $0' awktest
zhangsan 12 shanghai
lisi 33
beijing leaderwangwu 33 chengdu
# 输出文件名和参数个数
[root@server1 shell]# awk 'print FILENAME,ARGC' awktest
awktest 2
输出文件分隔符
当awk输出时,默认的分隔符
awk printf 格式化输出命令
- $s string字符串
- $d decimal 十进制
- $f float 浮点型
- $x hexa deciaml 十六进制
- $o 八进制
- $e 科学计数法
- $c 打印单个字符的ASCII码
- - 左对齐
-
- 右对齐
# 使用printf 输出,下面命令等同于print默认效果
awk 'BEGINFS=":"printf "%s\\n",$1' passwd
# 使用printf格式化输出
1. 左对齐
2. 每个变量固定20位长度
[root@server1 shell]# awk 'BEGINFS=":"printf "%20-s%20-s\\n",$1,$7' passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
#
# 第三位16进制输出,第四位十进制输出
[root@server1 shell]# awk 'BEGINFS=":"printf "hex: %x decimal: %d\\n",$3,$4' passwd
hex: c0 decimal: 192
hex: 51 decimal: 81
hex: 3e7 decimal: 997
模式匹配
- RegExp正则匹配
- 关系运算符
正则匹配
root❌0:0:root:/root:/bin/bash
operator❌11:0:operator:/root:/sbin/nologin
运算符匹配
- != 不等于
- == 等于
- ~ 匹配正则
- !~不匹配正则
# 输出第三行小于50的行
[root@server1 shell]# awk 'BEGINFS=":"$3<50print $0' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi
# 第七个字段为/bin/bash的符号
[root@server1 shell]# awk 'BEGINFS=":"$7="/bin/bash"print $0' passwd
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /bin/bash
daemon x 2 2 daemon /sbin /bin/bash
adm x 3 4 adm /var/adm /bin/bash
lp x 4 7 lp /var/spool/lpd /bin/bash
sync x 5 0 sync /sbin /bin/bash
shutdown x 6 0 shutdown /sbin /bin/bash
匹配正则表达式(强大)
打印第三个字段为3个数字的行,~代表匹配正则表达
[root@server1 shell]# awk 'BEGINFS=":"$3~/[0-9]3,/print $0' passwd
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
nginx:x:997:993:Nginx web server:/var/lib/nginx:/sbin/nologin
uwsgi:x:996:992:uWSGI daemon user:/run/uwsgi:/sbin/nologin
多个运算符
打印第一列为mail或者ftp的行
[root@server1 shell]# awk 'BEGINFS=":"$1=="ftp" || $1=="mail"print $0' passwd
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
非常强大,难点在于同一个功能多种实现,需要掌握一套方法,而不是所有方法。
表达式用法
普通的加减乘除都可以用,关注其效果即可
+
-
*
/
%
++x
x--
# 自定义变量控制和输出
[root@server1 shell]# awk 'BEGINvar=20;var2=30;var1="hello" ;print var,var1,var*var2'
20 hello 600
# 统计空白行
1. 匹配空白行
2. 每次匹配时执行sum+=
3. 执行完毕后执行END中命令
[root@server1 shell]# awk '/^$/sum;sum++ENDprint sum' /etc/services
17
求平均分
[root@server1 shell]# cat awktest
zhangsan 83 83 83 98
lisi 93 93 91 09
wangwu 93 93 02 02
[root@server1 shell]# awk 'BEGINprint "姓名","平均分"avg=($2+$3+$5)/3print $1,avg ' awktest
姓名 平均分
zhangsan 88
lisi 65
wangwu 62.6667
条件语句
语法格式
if(condition)
action1
else if
action2
else
action3
输出第三行大于50的行
[root@server1 shell]# awk 'if ($3 > 50 ) print $0' awktest
zhangsan 83 83 83 98
lisi 93 93 91 09
wangwu 93 93 02 02
循环语句
## do 循环
do while
do
actions
while(conition)
## for 循环
同java
计算 0到99的和,这里使用文件的方式执行awk命令
[root@server1 shell]# cat forsum.awk
BEGIN
for( i=0;i<100;i++)
sum = sum+i
print sum
[root@server1 shell]# awk -f forsum.awk
4950
计算平均值和每列的和
这里注意理解awk的变量传递。变量可以全局使用
[root@server1 shell]# awk -f forsum2.awk awktest
name chinaese math eng avg
zhangsan 83 83 83 83.00
lisi 93 93 91 92.33
wangwu 93 93 2 62.67
sum 269 269 176
[root@server1 shell]# cat forsum2.awk
BEGIN
printf "%-10s%-10s%-10s%-10s%-10s\\n","name","chinaese","math","eng","avg"
total=$2+$3+$4
avg=total/(NF-2)
printf "%-10s%-10d%-10d%-10d%-100.2f\\n",$1,$2,$3,$4,avg
sum1+=$2
sum2+=$3
sum3+=$4
END
printf "%-10s%-10d%-10d%-10d\\n","sum",sum1,sum2,sum3
字符串函数
函数名 | 解释 |
---|---|
length(str) | 返回长度 |
index(str1,str2) | 在str1中查找str2的位置,坐标1开始 |
tolower(str),toupper(str) | 大小写 |
substr(str,m,n) | 从str的m开始,截取n位 |
split(str,arr,fs) | 按照fs切割字符串,结果保存在arr中 |
match9(str,RE) | 在str中正则匹配,返回位置 |
sub(str,RE,new) | 替换字符,gsub全局替换 |
查询ea在字符串中位置
awk 'BEGINprint index("i have a dream","ea")'
用到时查询记得不一一介绍
常用选项
- -v 参数传递,引入外部变量
- -f 指定脚本文件
- -F 指定分隔符
- -V 查看版本号
# 使用外部参数是,需要先用-v引入
[root@server1 shell]# awk -v var=$var 'BEGINprint var'
hello
[root@server1 shell]# awk 'BEGINprint var'
数组用法
shell中数组用法
# 定义数组
[root@server1 shell]# array=("a" "b" "c")
# 获取数组长度
[root@server1 shell]# echo $#array[@]
3
# 获取单个元素
[root@server1 shell]# echo $array[1]
b
# 删除元素
[root@server1 shell]# unset array[2]
# 打印全部元素
[root@server1 shell]# echo $array[@]
a b
# 分片元素,访问下标1到3的元素
echo $array[@]:1:3
# 元素替换,所有小写e替换为大写E
$array[@]/e/E
# 数组遍历,同集合遍历
for i in array
do
echo $i
done
awk中数组用法
使用split分割字符串保存在数组中
[root@server1 shell]# awk 'BEGINstr="a b c d e";split(str,array," ");for(i in array) print array[i]'
d
e
a
b
c
awk中除了数字还能使用字符作为数组下标
观察下面例子,通过字符串作为数组下标可以更好的处理字符统计
[root@server1 shell]# awk -f sumtest1.awk awktest1
username success fail total
zhangsan 876090 6969 883059
wangwu 2436360 276396 2712756
lisi 3110107 2993939390 2997049497
[root@server1 shell]# cat sumtest1.awk
BEGIN
printf "%-10s%-20s%-20s%-20s\\n","username","success","fail","total"
SUCCESS[$2]+=$5
FAIL[$2]+=$7
TOTAL[$2]=SUCCESS[$2]+FAIL[$2]
END
for( i in SUCCESS)
printf "%-10s%-20d%-20d%-20d\\n",i,SUCCESS[i],FAIL[i],TOTAL[i]
[root@server1 shell]# head awktest1
user lisi update success 239239 fail 230303030
user lisi update success 239239 fail 230303030
user wangwu insert success 203030 fail 23033
user lisi update success 239239 fail 230303030
user lisi update success 239239 fail 230303030
user wangwu insert success 203030 fail 23033
user lisi update success 239239 fail 230303030
user lisi update success 239239 fail 230303030
user wangwu insert success 203030 fail 23033
user lisi update success 239239 fail 230303030
以上是关于shell三剑客grepsedawk精讲的主要内容,如果未能解决你的问题,请参考以下文章