三剑客
grep
grep 参数
-i 忽略字符大小写
-o 仅显示匹配到的字符串本身
-v 显示不能被匹配到的行
-E 支持使用扩展的正则表达式元字符
-q 不输出任何信息
-c 统计文本中与之相关的有几行
参数 | 效果 |
---|---|
-i | 忽略大小写 |
-l | 只列出匹配行所在的文件名 |
-n | 在每一行前面加上它在文件中的相对行号 |
-c | 显示成功匹配的行数 |
-s | 禁止显示文件不存在或文件不可读的错误信息 |
-q | 静默? |
-v | 反向查找,只显示不匹配的行 |
-R,-r | 递归针对目录 |
–color | 颜色 |
-o | 只显示匹配的内容 |
-B | print NUM lines of leading context |
-A | print NUM lines of trailing context |
-C | print NUM lines of output contesx |
-c
将passwd重定向到pass.txt
统计文本中与root相关的行有多少
-c 计数
grep -i -c root ./pass.txt
-n
-n 显示行号
输出文本非空行的内容
grep '^$' -n -v pass.txt
显示匹配内容在文件的哪一行
[root@wzb ~]# grep -n 'root' /etc/passwd /etc/group
/etc/passwd:1:root:x:0:0:root:/root:/bin/bash
/etc/passwd:10:operator:x:11:0:operator:/root:/sbin/nologin
/etc/group:1:root:x:0:
[root@wzb ~]# vim /etc/passwd +10 #+n 可以直接去到相应的行编辑,可用在查看报错修改等地方
-R
查找出目录下所有的相关的文件
这里找在这个目录下所有文件中带有=~
的文件
:的左边是文件,右边是相关内容的行号
[root@wzb ~]# grep -R '=~' /home/wzb/practice/shell/
/home/wzb/practice/shell/nginx_select.sh:elif [[ ! "$n" =~ [1-3] ]];then
/home/wzb/practice/shell/useradd_04.sh: if [[ ${num} =~ ^[1-9][0-9]+$ || -n ${num} || ${num} =~ [1-9] ]];then
另一种方法
[root@wzb ~]# grep '=~' /home/wzb/practice/shell/*
/home/wzb/practice/shell/lamp_or_lnmp.sh:[[ ! "$num" =~ [1-3] ]] && {
/home/wzb/practice/shell/nginx_select.sh:elif [[ ! "$n" =~ [1-3] ]];then
-l
-l 列出含有所指定参数的文件,不显示具体内容
[root@wzb ~]# grep -l 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd
/etc/shadow
-v
反向查找,只显示不匹配的行
[root@svr7 shell]# cat txt1
root
wzb
123
root
[root@svr7 shell]# grep -v 'root' txt1
wzb
123
grep 查看上下文
-C2 查看上下文2行 context
-B2 查看上2行 before
-A2 查看下2行 after
[root@wzb ~]# grep -C2 'root' /etc/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
--
halt:x:7:0:halt:/sbin:/sbin/halt
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
正则表达式
通过 grep 来理解一下正则表达式
[root@wzb tongzi]# grep '^root' /etc/passwd #找出以root开头的行
root:x:0:0:root:/root:/bin/bash
[root@wzb tongzi]# grep '^[rc]oot' /etc/passwd #找出以root或coot开头的行
root:x:0:0:root:/root:/bin/bash
[root@wzb tongzi]# grep '^[^ab]oot' /etc/passwd #找出开头不是 aoot 和 boot 的行
root:x:0:0:root:/root:/bin/bash
显示所有 # 开头的行
grep '^#' /etc/vsftpd/vsftpd.conf
-v 取反 显示没有被注释的行
grep -v '^#' /etc/vsftpd/vsftpd.conf
grep -r 递归针对目录
扩展正则表达式 grep -E
扩展正则表达式ERE集合
扩展正则必须使用grep -E才能生效
字符 | 作用 |
---|---|
+ | 匹配前一个字符1次或多次,前面字符至少出现1次 |
[😕]+ | 匹配括号内的":“或者”/"字符1次或多次 |
? | 匹配前一个字符0次或1次,前面字符可有可无 |
|(竖线) | 表示或者,同时过滤多个字符串 |
() | 分组过滤,被括起来表示一个整体 |
a{n,m} | 匹配前一个字符最少n次,最多m次 |
a{n,} | 匹配前一个字符最少n次 |
a{n} | 匹配前一个字符正好n次 |
a{,m} | 匹配前一个字符最多m次 |
素材文件
[root@test /home/wzb/my_prac]# cat 2022-4-20
god
goooooooooooooooooood
goooooooood
glad
good
goooooooood
gd
x
y
love 123 love
love 1
love 11 love
lovelove
+
+号 匹配前一个字符1次或多次
[root@test /home/wzb/my_prac]# grep -E '+g' 2022-4-20
god
goooooooooooooooooood
goooooooood
glad
good
goooooooood
gd
?
?号 匹配前一个字符0次或1次
[root@test /home/wzb/my_prac]# grep -E "g?d" 2022-4-20
god
goooooooooooooooooood
goooooooood
glad
good
goooooooood
gd
|
| 符:或者的意思
找出文本带有x或y的行
[root@test /home/wzb/my_prac]# grep -E "x|y" 2022-4-20
x
y
()
()括号,分组过滤被括起来的内容,括号内的内容表示一个整体
\1 :表示从左侧起,第一个括号中的模式所匹配到的字符
\2 : 表示第二个括号
找出文本中带有good
或者glad
的行
[root@test /home/wzb/my_prac]# grep -E "g(oo|la)d" 2022-4-20
glad
good
找出文本中含有两个love的行
[root@wzb my_prac]# grep -E "(l..e).*\1" 2022-4-20
love 123 love
love 11 love
lovelove
效果等同于
[root@wzb my_prac]# grep -E "(l..e).*(l..e)" 2022-4-20
love 123 love
love 11 love
lovelove
{}
{n,m}匹配次数
匹配 o 字符 ,最少2次,最多4次
[root@wzb my_prac]# grep -E "o{2,5}" 2022-4-20
goooooooooooooooooood
goooooooood
good
goooooooood
-m m次匹配后停止
[root@wzb my_prac]# grep -E -m 2 "yu" pass.txt
yu
yu2
找出两位数或者三位数
grep -E "[0-9]{2,3}" pass.txt
发现会有四位数甚至更多
仅仅找出两位数或三位数,不包含其他位数
grep -E "\<[0-9]{2,3}\>" pass.txt
找出文件中以空格开头的行
空格:[[:space:]]
[root@wzb my_prac]# grep -E "^[[:space:]].*" 2022-4-20
1
1
11
在passwd文件中,找出用户名和shell(解释器)相同的用户
以 非冒号 开头的行 到冒号就结束 即判断用户名
[^:]+
:读取非冒号的字符一次或多次
[root@wzb my_prac]# grep -E "^([^:]+\>).*\1$" pass.txt
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
grep & 正则 查找IP
这里只找到了1个ip
这个错误是因为索引为1的变量被重复引用,所以只有4个数字都相同的ip才会被识别
[root@wzb ~]# grep -E '([0-9]{1,3})\.\1.\1.\1' /etc/sysconfig/network-scripts/ifcfg-ens33
DNS1=114.114.114.114
正确的方式
[root@wzb ~]# grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=192.168.92.5
NETMASK=255.255.255.0
GATEWAY=192.168.92.2
DNS1=114.114.114.114
查找网卡文件下,本机的IP地址
[root@wzb ~]# grep -E '^IPA' /etc/sysconfig/network-scripts/ifcfg-ens33 | grep -E -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
192.168.92.5
sed
参数
sed :对文件或者数据流进行加工处理
语法:sed [选项] [sed内置命令字符] [输入文件]
参数选项 | 解释 |
---|---|
-n | 取消默认sed的输出,常与sed内置命令p一起用 |
-i | 直接将修改结果写入,不用-i,sed修改的是内存数据 |
-e | 多次编辑,不需要管道符 |
-r | 支持正则扩展 |
内置字符
sed常用内置命令字符
sed内置命令字符 | 解释 |
---|---|
a | append,对文本追加,在指定行后面添加一行/多行文本 |
d | delete,删除匹配行 |
i | insert,表示插入文本,在指定前添加一行/多行文本 |
p | print,打印匹配行的内容,通常p与-n一起用 |
s/正则/替换内容/g | 匹配正则内容,然后替换内容(支持正则),结尾g代表全局匹配 |
r | read,将文件的内容插入到指定行的后面 |
w | 将指定内容另存为 |
c | 替换 |
sed匹配范围
范围 | 解释 |
---|---|
空地址 | 全文处理 |
单地址 | 指定文件某一行 |
/pattern/ | 被模式匹配到的每一行 |
范围区间 | 10,20 10到20行 , 10,+5 第10行向下5行 , /pattern1/,/pattern2/ |
步长 | 1~2 表示1、3、5、7奇数行,2~2 表示2、4、6、8偶数行 |
参数的使用
-n & p
sed -n '2,4p' user # 输出2-4行
sed -n '2p;4p' user # 输出第2行和第4行
sed -n '3,+1p' user # 输出第3行以及后面1行
sed -n '/^root/' user # 输出以root开头的行
sed -n '/root/p' user # 输出包含root的行
sed -nr '/^root|^bin/p' user # 输出以root或者bin开头的行(|是扩展正则,需要r选项)
d (删除)
sed '3,5d' a.txt # 删除第3行到第5行
sed '/xml/d' a.txt # 删除与xml相关的行
sed '/xml/!d' a.txt # 删除与xml不相关的行
sed '/^install/d' a.txt # 删除以install开头的行
sed '$d' a.txt # 删除最后一行
sed '/^$/d' a.txt # 删除空行
s///(替换)
cat /etc/passwd > pass.txt
将 pass.txt 中前10行 的 bin 用户 改为 C
[root@wzb practice]# sed -r '1,10s/^bin/C/g' pass.txt
将前10行bin开头的用户改为C,将m开头的改为M
[root@wzb practice]# sed -r -e '1,10s/^bin/C/g' -e '1,10s/^m/M/' pass.txt
内置字符的使用
r
r /etc/hosts #读取相应文件写入
sed -r '2r /etc/hosts' a.txt #从第2行开始读入
sed -r '/2/r /etc/hosts' a.txt #匹配到2的行,开始读入
w
w /home/wzb/cc.txt #将文件另存为
sed -r '/north/w newfile' datafile #将有关north的行另存为
sed -r '/3,$w/w newfile' datafile #将第3行到最后另存为
a
sed -r '2a 123' /etc/hosts #在第2行后面追加
i
sed -r '2i 123' /etc/hosts #在第2行前面追加
c
sed -r '2c 123' /etc/hosts #将第2行替换
=(输出行号)
输出行号
这个文件有4行
[root@svr7 shell]# sed -n "$=" txt1
4
和root相关的行在第1和第4行
[root@svr7 shell]# sed -n "/root/=" txt1
1
4
h H g G 暂存和取用
指令 | 效果 |
---|---|
h | 将内容覆盖至暂存空间 |
H | 将内容追加至暂存空间 |
g | 将暂存空间的内容覆盖至模式空间 |
G | 将暂存空间的内容追加至模式空间 |
将第1行内容放在暂存空间,然后G使暂存空间的内容追加
效果就是将第1行的内容复制到了最后一行
[root@wzb practice]# sed -r '1h;$G' pass_short.txt
与上面相似,将内容覆盖至暂存空间后删除
效果是将第一行的内容剪切到了最后一行
[root@wzb practice]# sed -r '1{h;d};$G' pass_short.txt
将第1行…然后将第2行至最后的所有的行都覆盖为暂存空间的内容
效果就是所有行都变为第1行
[root@wzb practice]# sed -r '1h;2,$g' pass_short.txt
将第1行覆盖,2-3行追加,
效果就是在最后追加了前3行的内容
[root@wzb practice]# sed -r '1h;2,3H;$G' pass_short.txt
空间内容互换(反人类)
[root@wzb practice]# sed -r '4h;5x;6G' u.txt
awk
awk 擅长文本格式化,且输出格式化后的结果
awk 内置变量
内置变量 | 解释 |
---|---|
FS | 输入字段分隔符,默认为空白字符 |
OFS | 输出字段分隔符,默认为空白字符 |
RS | 输入记录分隔符(输入换行符),指定输入时的换行符 |
ORS | 输出记录分隔符(输出换行符),输出时用指定符号替代换行符 |
NF | NF:number of Field,当期行的字段的个数(即当前行被分割成了几列),字段数量 |
NR | NR:number of records,行号,当前处理的文本的行号 |
FNR | FNR:各文件分别计数的行号 |
FIIENAME | 当前文件名 |
ARGC | 命令行各个参数 |
ARGV | 数组,保存的事命令行所给定的各个参数 |
$n | 指定分隔符后,当前记录的第n个字段 |
$0 | 完整的输入记录 |
FS | 字段分隔符,默认是空格 |
内置变量 | 解释 |
---|---|
$n | 指定分隔符后,当前记录的第n个字段 |
$0 | 完整的输入记录 |
FS | 字段分隔符,默认是空格 |
NF(Number of fields) | 分割后,当前行一共有多少个字段 |
NR(Number of records) | 当前记录数,行数 |
更多内置变量可以man手册查看 | man awk |
awk参数
参数 | 解释 |
---|---|
-F | 指定分割字段符 |
-v | 定义或修改一个awk内部的变量 |
-f | 从脚本文件中读取awk命令 |
[root@wzb my_prac]# cat awk.txt
1 2 3 4
5 6 7 8
9 a b c
d e f g
输出全部内容
[root@wzb my_prac]# awk '{print $0}' awk.txt
1 2 3 4
5 6 7 8
9 a b c
d e f g
输出第1列的内容
[root@wzb my_prac]# awk '{print $1}' awk.txt
1
5
9
d
输出多行数据
[root@wzb my_prac]# awk '{print $1,$3}' awk.txt
1 3
5 7
9 b
d f
pass
[root@wzb my_prac]# awk '{print "第一列:"$1,"第二列:$3"}' awk.txt
第一列:1 第二列:$3
第一列:5 第二列:$3
第一列:9 第二列:$3
第一列:d 第二列:$3
指定分隔符
[root@wzb my_prac]# cat awk2.txt
1#2#3#4
5#6#7#8
9#a#b#c
d#e#f#g
[root@wzb my_prac]# awk -F '#' '{print $1,$3}' awk2.txt
1 3
5 7
9 b
d f
显示文件第2、3行
[root@wzb my_prac]# awk 'NR==2,NR==3{print $0}' awk1.txt
5 6 7 8
9 a b c
显示行号
[root@wzb my_prac]# awk '{print NR,$0}' awk1.txt
1 1 2 3 4
2 5 6 7 8
3 9 a b c
4 d e f g
显示第一列和倒数第二列
[root@wzb my_prac]# awk '{print $1,$(NF-1)}' awk1.txt
输出第2至第4行
[root@wzb my_prac]# awk 'NR==2,NR==4{print}' awk1.txt
5 6 7 8
9 a b c
d e f g
利用awk取出ip地址
[root@wzb my_prac]# awk 'NR==2{print $2}' ip.txt
192.168.92.5
OFS:指定输出分隔符
指定输出的分隔符为—
[root@wzb my_prac]# sed "1,5p" pass1.txt -n | awk -F ":" -v OFS="---" '{print $1,$NF}'
root---/bin/bash
bin---/sbin/nologin
daemon---/sbin/nologin
adm---/sbin/nologin
lp---/sbin/nologin
制表符 \t
[root@wzb my_prac]# sed "1,5p" pass1.txt -n | awk -F ":" -v OFS="\t" '{print $1,$NF}'
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
打印前三行
[root@wzb my_prac]# awk 'NR<4{print $0}' pass1.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
指定输入和输出符
[root@wzb my_prac]# sed "1,5p" -n pass.txt | awk -v FS=':' -v OFS='---' '{print NR,NF,$1}'
1---7---root
2---7---bin
3---7---daemon
4---7---adm
5---7---lp
FNR 处理多个文件显示行号
每个文件都重新处理行号
[root@wzb practice]# awk '{print FNR,$0}' 1 2
1 a
2 b
3 c
1 w
2 x
3 y
4 z
读取空格,将空格作为换行符输出
awk -v RS=' ' '{print NR,$0}' mess_#
ORS,修改每一行的结束符号,将换行改为@
[root@wzb my_prac]#awk -v ORS="@" '{print NR,$0}' mess_#
1 1 2 3 4@2 5 6 7 8@3 9 a b c@4 d e f g@
ORS 将文件中的内容变为一行
[root@wzb practice]# awk 'BEGIN{ORS=""} {print $0}' mess_#
1 2 3 45 6 7 89 a b cd e f g
OR 将文件分为多行
[root@wzb practice]# cat 1
a:b:c
111 222 333 444 555
[root@wzb practice]# awk -F : 'BEGIN {RS=" "} {print $0}' 1
a:b:c
111
222
333
444
555
FILENAME
显示awk正在处理文件的名字
文件名为 version.txt mess_#
[root@wzb my_prac]# awk '{print FNR,FILENAME,$0}' mess_# version.txt
1 mess_# 1 2 3 4
2 mess_# 5 6 7 8
3 mess_# 9 a b c
4 mess_# d e f g
1 version.txt CentOS Linux release add 7.9.2009 (Core)
变量ARGC、ARGV
BEGIN
[root@wzb my_prac]# awk 'BEGIN{print "开始用awk"} {print $0}' mess_#
开始用awk
1 2 3 4
5 6 7 8
9 a b c
d e f g
ARGV
ARGV是作为一个列表
发现ARGV中存了 awk 、执行文件名、空格
[root@wzb my_prac]# awk 'BEGIN{print "开始"} {print ARGV[0],$0}' mess_#
开始
awk 1 2 3 4
awk 5 6 7 8
awk 9 a b c
awk d e f g
再次执行,发现一次输出的是 awk,文件名,文件名
awk 'BEGIN{print "开始"} {print ARGV[0],ARGV[1],ARGV[2]}' mess_# pass_short.txt
开始
awk mess_# pass_short.txt
awk mess_# pass_short.txt
awk mess_# pass_short.txt
awk mess_# pass_short.txt
自定义变量(外部变量)
-v 指定变量
[root@wzb my_prac]# awk -v myname="wzb" 'BEGIN{print "我的名字是?",myname}'
我的名字是? wzb
间接引用shell变量
[root@wzb my_prac]# myname=wzb
[root@wzb my_prac]# awk -v ment=$myname 'BEGIN{print ment} {print $0}' mess_#
wzb
1 2 3 4
5 6 7 8
9 a b c
d e f g
awk格式化
printf
不会输出换行符
[root@wzb my_prac]# awk '{printf $0 }' article_1
床前明月光疑是地上霜举头望明月低头思故乡
给 printf 添加格式
[root@wzb my_prac]# awk '{printf "%s\n",$0 }' article_1
床前明月光
疑是地上霜
举头望明月
低头思故乡
awk的 格式替换符 想要修改多个变量,必须传⼊多个
并且awk后若不加上文件数据,必须添加 BEGIN
%d 表示十进制数字
[root@wzb my_prac]# awk 'BEGIN{printf "%d\n%d\n%d\n%d\n",1,2,3,4}'
1
2
3
4
案例
[root@wzb my_prac]# awk '{printf "first:%s second:%s thrid:%s fourth:%s\n",$1,$2,$3,$4}' mess_#
first:1 second:2 thrid:3 fourth:4
first:5 second:6 thrid:7 fourth:8
first:9 second:a thrid:b fourth:c
first:d second:e thrid:f fourth:g
案例
%s\t 格式化字符串,添加制表符,4个空格
-:向左对齐,25个字符长度
[root@wzb my_prac]#awk -F ":" 'BEGIN{printf "%-10s\t %-10s\t %-10s\t %-10s\t %-10s\t %-10s\t %-s\n","用户名","密码","UID","GID","用户注释","用户家目录","用户使用的解释器"} {printf "%-10s\t %-10s\t %-10s\t %-10s\t %-10s\t %-20s\t %s\n",$1,$2,$3,$4,$5,$6,$7}' pass_short.txt
用户名 密码 UID GID 用户注释 用户家目录 用户使用的解释器
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
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
mail x 8 12 mail /var/spool/mail /sbin/nologin
operator x 11 0 operator /root /sbin/nologin
awk模式
BEFIN、END
案例
[root@wzb my_prac]# awk 'BEGIN{print "处理文本之前的操作"}{print $0}END{print "done"}' mess_#
处理文本之前的操作
1 2 3 4
5 6 7 8
9 a b c
d e f g
done
打印前三行的内容
[root@wzb my_prac]# awk 'NR<4{print $0}' pass1.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
!= 的用法,不输出第一行
[root@wzb my_prac]# awk 'NR!=1{print $0}' mess_#
5 6 7 8
9 a b c
d e f g
正则表达式的用法
找出以wzb开头的用户
用法:awk /正则表达式/{动作} 文件
[root@wzb my_prac]# awk '/^wzb/{print $0}' pass1.txt
wzb:x:1000:1000:wzb:/home/wzb:/bin/bash
wzb123:x:1004:1004::/home/wzb123:/bin/bash
案例
[root@wzb my_prac]# awk -F ":" '/^wzb/{print $1,$NF}' pass1.txt
wzb /bin/bash
wzb123 /bin/bash
逻辑操作符和复合模式
~ 表示正则
[root@wzb practice]# awk -F ":" '$1~/root/ && $3 <= 15' /etc/passwd
...
[root@wzb practice]# awk -F ":" '$1~/root/ || $3 <= 15' /etc/passwd
...
[root@wzb practice]# awk -F ":" '!($1~/root/ || $3 <= 15)' /etc/passwd
...
awk脚本编程
if
判断root用户
[root@wzb practice]# awk -F ":" '{if($3==0) {print $1 " is admin"}}' /etc/passwd
root is admin
统计用户数量 要注意大括号的位置 行处理为一个部分,END为一个部分
[root@wzb practice]# awk -F ":" '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd
47
注意大括号的位置
[root@wzb practice]# awk -F ":" '{if($3==0){count++} else{i++}} END{print "管理员个数:"count;print "系 统用户数:" i}' /etc/passwd
管理员个数:1
系统用户数:53
if多分支语句
[root@wzb practice]# awk -F ":" '{if($3==0){i++} else if($3>999){k++}else{j++}} END{print "管理员个数:"i;print "普通用户个数:"k;print "系统用户:"j}' /etc/passwd
管理员个数:1
普通用户个数:6
系统用户:47
while
while简单应用
[root@wzb practice]# awk 'BEGIN{ i=1;while(i<=3){print i;i++} }'
1
2
3
案例
[root@wzb practice]# awk -F ":" '/^root/{i=1;while(i<=7){print $i;i++}}' pass1.txt
root
x
0
0
root
/root
/bin/bash
每行打印10次
[root@wzb practice]# awk -F ":" '{i=1;while(i<=10){print $0;i++}}' /etc/passwd
案例
[root@wzb practice]# cat b.txt
111 222
333 444 555
666 777 888 999
案例 依次打印每一列
将一行打印完再打印下一行的内容
[root@wzb practice]# awk '{i=1;while(i<=NF){print $i;i++}}' b.txt
111
222
333
444
555
666
777
888
999
for
c风格for
[root@wzb practice]# awk 'BEGIN{for(i=1;i<=5;i++){print i}}'
1
2
3
4
5
每行打印10次
[root@wzb practice]# awk '{ for(i=1;i<=10;i++){print $0} }' b.txt
依次打印每一列
[root@wzb practice]# awk '{ for(i=1;i<=NF;i++){print $i} }' b.txt
111
222
333
444
555
666
777
888
999
awk & 数组
案例
[root@wzb practice]# awk -F ":" '{username[i++]=$1} END{print username[0]}' /etc/passwd
root
案例
[root@wzb practice]# awk -F ":" '{username[x++]=$1} END{for(i in username) {print i,username[i]}}' pass1.txt0 root
1 bin
2 daemon
统计passwd中的解释器 for语句
[root@wzb practice]# awk -F ":" '{list[$NF]++} END{for(i in list){print i,list[i]}}' /etc/passwd
/bin/sync 1
/bin/bash 6
/sbin/nologin 45
/sbin/halt 1
/sbin/shutdown 1
网站访问状统计
[root@wzb practice]# netstat -ant | grep ":80" | awk '{list[$NF]++} END{print $NF,list[$NF]}'
LISTEN 3
统计当前访问ip的数量
[root@wzb ~]# ss -ant | grep ':80' | awk '{list[$(NF-1)]++} END{for (i in list) {print i,list[i]}}'
*:8081 1
*:8082 1
*:80 1
add
-k2 表示根据第二列排序
[root@wzb ~]# ss -ant | grep ':80' | awk '{list[$(NF-1)]++} END{for (i in list) {print i,list[i]}}' | sort -k2 -rn
脚本编程实战
统计Nginx日志中某一天不同ip的访问量
这里指在2022.8.6这一天
[root@wzb logs]# grep '06/Aug/2022' text.log | awk '{list[$1]++} END{ for (i in list){print i,list[i]} }'
192.168.92.1 12
192.168.92.5 3
192.168.92.160 4
another
[root@wzb logs]# awk '/06\/Aug\/2022/{print $0}' text.log | awk '{list[$1]++} END{for (i in list){print i,list[i]}}'
192.168.92.1 12
192.168.92.5 3
192.168.92.160 5
统计用户名为4个字符的用户
[root@wzb practice]# awk -F ":" '$1~ /^....$/{count++;print $1} END{print "count is: " count}' /etc/passwd
root
sync
halt
mail
dbus
abrt
qemu
sssd
sshd
test
count is: 10
若要统计10个字符就会很麻烦
[root@wzb practice]# awk -F ":" 'length($1)==4{count++;print $1} END{print "count is "count}' /etc/passwd
awk命令执行流程
使用 grep 找出关于 sbin/nologin 的行
[root@wzb my_prac]# grep "sbin/nologin" pass_short.txt -n
2:bin:x:1:1:bin:/bin:/sbin/nologin
3:daemon:x:2:2:daemon:/sbin:/sbin/nologin
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
7:bin:x:1:1:bin:/bin:/sbin/nologin
8:daemon:x:2:2:daemon:/sbin:/sbin/nologin
9:adm:x:3:4:adm:/var/adm:/sbin/nologin
10:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
14:mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
15:operator:x:11:0:operator:/root:/sbin/nologin
现用 awk 来实现
//之间要写正则,要用"“将”/"转为普通的符号
[root@wzb my_prac]# awk '/\/sbin\/nologin/{print NR,$0}' pass_short.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
7 bin:x:1:1:bin:/bin:/sbin/nologin
8 daemon:x:2:2:daemon:/sbin:/sbin/nologin
9 adm:x:3:4:adm:/var/adm:/sbin/nologin
10 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
14 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
15 operator:x:11:0:operator:/root:/sbin/nologin
多个正则,打印出 以bin开头 到 以daemon开头 的行
发现若存在多个 daemon 的情况下,会输出到最后一个daemon
[root@wzb my_prac]# awk '/^bin/,/^daemon/{print $0}' pass_short.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
企业实现Nginx日志
1、统计访客日志的ip数量
awk '{print $1}' my.log | sort -n
去重
[root@wzb logs]# awk '{print $1}' my.log | sort -n | uniq
127.0.0.1
192.168.92.1
192.168.92.5
计算有多少行
[root@wzb logs]# awk '{print $1}' my.log | sort -n | uniq | wc -l
3
2、查看访问最频繁的前10个ip
首先可以查看每个ip出现的次数
uniq -c 显示次数
sort -nr 从大到小排列
head -10 显示前10行
[root@wzb logs]# awk '{print $1}' my.log | sort -n | uniq -c | sort -nr | head -10
37 192.168.92.1
24 127.0.0.1
6 192.168.92.5
awk练习
打印出普通用户的用户和家目录
普通用户的d大于1000
[root@wzb my_prac]# awk -F ":" '$3>=1000{print $1,$(NF-1)}' pass1.txt
nfsnobody /var/lib/nfs
wzb /home/wzb
test /home/test
tom /home/tom
itheima /home/itheima
wzb123 /home/wzb123
给文件的前5行添加“#”
[root@wzb my_prac]# awk 'NR<6{print "#",$0}' article_1
# 床前明月光
# 疑是地上霜
# 举头望明月
# 低头思故乡
# 床前明月光
查看练习文本
依次是 人名 区号 电话号码 三次捐款的数额
[root@wzb my_prac]# cat tel.txt
Mike Harrington:[510] 548-1278:250:100:175
Christian Dobbins:[408] 538-2358:155:90:201
Susan Dalsass:[206] 654-6279:250:60:50
Archie McNichol:[206] 548-1348:250:100:175
Jody Savage:[206] 548-1278:15:188:150
Guy Quigley:[916] 343-6410:250:100:175
Dan Savage:[406] 298-7744:450:300:275
Nancy McNeil:[206] 548-1278:250:80:75
John Goldenrod:[916] 348-4278:250:100:175
Chet Main:[510] 548-5258:50:95:135
Tom Savage:[408] 926-3456:250:168:200
Elizabeth Stachelin:[916] 440-1763:175:75:300
显示所有电话号码
[root@wzb my_prac]# awk -F ":" '{print $2}' tel.txt | awk '!/^$/{print $2}'
548-1278
538-2358
654-6279
548-1348
548-1278
343-6410
298-7744
548-1278
348-4278
548-5258
926-3456
440-1763
第二种方法
-F “[ :]” 指定分隔符是“空格”或者":"
!/^$/ 将空字符不显示
[root@wzb my_prac]# awk -F "[ :]" '!/^$/{print $4}' tel.txt
548-1278
538-2358
654-6279
548-1348
548-1278
343-6410
298-7744
548-1278
348-4278
548-5258
926-3456
440-1763
显示Tom的电话
通过 正则 /^Tom/ 来找到与Tom相关的行
[root@wzb my_prac]# awk '/^Tom/{print $0}' tel.txt | awk -F ":" '{print $2}' | awk '{print $2}'
926-3456
显示Nancy的姓名、区号、电话
[root@wzb my_prac]# awk -F "[ :]" '/^Nancy/{print $1,$2,$3,$4}' tel.txt
Nancy McNeil [206] 548-1278
显示D开头的姓
$2~/^D/ 对第2列使用正则
[root@wzb my_prac]# awk -F "[ :]" '$2~/^D/{print $2}' tel.txt
Dobbins
Dalsass
显示区号是 916 的人名
[root@wzb my_prac]# awk -F ":" '$2~/\[916\]/{print $1}' tel.txt
Guy Quigley
John Goldenrod
Elizabeth Stachelin
显示Mike的捐款信息,在每一次捐款钱加上”$“
格式化输出
[root@wzb my_prac]# awk -F ":" '$1~/^Mike/{printf "$%s $%s $%s\n",$(NF-2),$(NF-1),$(NF)}' tel.txt
$250 $100 $175
显示所有人的 姓 + 逗号 + 名
[root@wzb my_prac]# awk -F ":" '{print $1}' tel.txt | awk '!/^$/{printf "%s,%s\n",$1,$2}'
Mike,Harrington
Christian,Dobbins
Susan,Dalsass
Archie,McNichol
Jody,Savage
Guy,Quigley
Dan,Savage
Nancy,McNeil
John,Goldenrod
Chet,Main
Tom,Savage
Elizabeth,Stachelin
删除文件的空白行
[root@wzb my_prac]# awk '!/^$/{print $0}' tel.txt
Mike Harrington:[510] 548-1278:250:100:175
Christian Dobbins:[408] 538-2358:155:90:201
Susan Dalsass:[206] 654-6279:250:60:50
Archie McNichol:[206] 548-1348:250:100:175
Jody Savage:[206] 548-1278:15:188:150
Guy Quigley:[916] 343-6410:250:100:175
Dan Savage:[406] 298-7744:450:300:275
Nancy McNeil:[206] 548-1278:250:80:75
John Goldenrod:[916] 348-4278:250:100:175
Chet Main:[510] 548-5258:50:95:135
Tom Savage:[408] 926-3456:250:168:200
Elizabeth Stachelin:[916] 440-1763:175:75:300
使用if语句 完整的写法
[root@wzb practice]# awk -F ":" '{ if($3<20){print $3} else {print $1} }' /etc/passwd
sed 案例练习
案例
创建素材文件
[root@wzb my_prac]# cat 2022-4-21
My name is chaoge.
I teach linux.
I like play computer game.
My qq is 23123123a
My web is www.baidu.com
打印第二行的内容
[root@wzb my_prac]# sed "2p" 2022-4-21 -n
I teach linux.
查看2-3行的内容
[root@wzb my_prac]# sed "2,3p" 2022-4-21 -n
I teach linux.
I like play computer game.
找出与linux相关的行,并打印
-n 输出相关结果
[root@wzb my_prac]# sed "/linux/p" 2022-4-21 -n
I teach linux.
删除有关linux的行
d:删除
i:将修改写入文件
[root@wzb my_prac]# sed "/linux/d" 2022-4-21 -i
[root@wzb my_prac]# cat 2022-4-21
My name is chaoge.
I like play computer game.
My qq is 23123123a
my web is www.baidu.com
删除第五行之后的所有内容,包括第5行
$:表示结尾
d:表示删除
使用单引号
[root@wzb my_prac]# sed '5,$d' 2022-4-21 -i
[root@wzb my_prac]# cat 2022-4-21
1My name is chaoge.
2I like play computer game.
3My qq is 23123123a
4my web is www.baidu.com
将My 替换为his
s/要被替换的内容/替换的内容/g
g表示全局
[root@wzb my_prac]# sed "s/My/his/g" 2022-4-21
his name is chaoge.
I teach linux.
I like play computer game.
his qq is 23123123a
his web is www.baidu.com
将My改为his,将linux改为python
用到 -e
[root@wzb my_prac]# sed -e "s/My/his/g" -e "s/linux/python/g" 2022-4-21
me is chaoge.
I teach python.
I like play computer game.
his qq is 23123123a
his web is www.baidu.com
在第二行添加文本
2a:表示在第二行之后添加,那么第三行则显示nice
[root@wzb my_prac]# sed "2a nice" 2022-4-21
My name is chaoge.
I teach linux.
nice
I like play computer game.
My qq is 23123123a
My web is www.baidu.com
在第四行添加nice
i:表示insert,第四行显示nice
[root@wzb my_prac]# sed "4i nice" 2022-4-21
My name is chaoge.
I teach linux.
I like play computer game.
nice
My qq is 23123123a
My web is www.baidu.com
添加多行信息
[root@wzb my_prac]# sed "4i add1\nadd2" 2022-4-21
My name is chaoge.
I teach linux.
I like play computer game.
add1
add2
My qq is 23123123a
My web is www.baidu.com
每一行都添加信息
a后面有无空格都可以
[root@wzb my_prac]# sed "a ==========" 2022-4-21
My name is chaoge.
==========
I teach linux.
==========
I like play computer game.
==========
My qq is 23123123a
==========
My web is www.baidu.com
==========
删除第4行后面所有,包括第4行
sed "4,$d" 2022-4-21 -i
删除从root开始,到ftp之间的行
sed "/^root/,/^ftp/d" pass.txt
找出网卡的ip
[root@wzb my_prac]# ifconfig ens33 | sed "2p" -n
inet 192.168.92.5 netmask 255.255.255.0 broadcast 192.168.92.255
sed "s/^inet.*//g
"将开头所有字符到 inet
为止的字符替换为空
sed "s/netmask.*//g
将netmask
之后的所有字符替换为空
[root@wzb my_prac]# ifconfig ens33 | sed "2p" -n | sed "s/^.*inet//g" | sed "s/netmask.*//g"
192.168.92.5
使用 -e
2s 在第二行中进行替换
[root@wzb my_prac]# ifconfig ens33 | sed -e "2s/^.*inet//gp" -n
192.168.92.5 netmask 255.255.255.0 broadcast 192.168.92.255
[root@wzb my_prac]# ifconfig ens33 | sed -e "2s/^.*inet//g" -e "2s/net.*//gp" -n
192.168.92.5
将空行添加#
将文件中空白字符开头的行,添加注释
原理是将开头的一个空格替换成#,并不是在开头插入一个#
[root@wzb my_prac]# sed -e "s/^[[:space:]]/#/g" 2022-4-21 -e "s/^$/#/g" 2022-4-21 -i
[root@wzb my_prac]# cat 2022-4-21
1
2
3
4
#5
6
#
找出系统版本
找出系统版本
将 release之前的文本和后面的空格再加上第一个读取到的非.的字符,以及后面的所有字符
替换为 第一个读取到的字符 即为 7
[root@wzb my_prac]# sed -r "s/^.*release[[:space:]]([^.]).*/\1/p" /etc/centos-release -n
7
若有多个空格,使用 [[:space:]]*
将多个空格替换
[root@wzb my_prac]# cat version.txt
CentOS Linux release 7.9.2009 (Core)
[root@wzb my_prac]# sed -r "s/^.*release[[:space:]]*([^.]).*/\1/p" version.txt -n
7
[root@wzb my_prac]# sed -r -n "s/^.*release *(.*)/\1/p" version.txt # 也可以不用[[:space:]]
删除文件的空白行和注释行
有空格的行无法删除
[root@wzb my_prac]# sed -e "/^$/d" 2022-4-21 -e "/^#/d" 2022-4-21 -i
[root@wzb my_prac]# cat 2022-4-21
1
2
3
4
6
things
使用分号
sed "/^#/d";"/^$/d" 2022-4-21
给文件前三行添加$符号
-r 扩展正则表达式 "\1"需要
先将第一个字符替换为#加读取到的第一个字符
[root@wzb practice]# sed -r '1,3s/(^.)/$\1/' 2022-4-21
$#
$1
$#
2
#
3
4
6
things
另一种方法
似乎-r 和 -e 无法连用?
错误的
[root@wzb my_prac]# sed "1,3p" 2022-4-21 -n | sed -r "s/(^.)/@\1/p" -n
@#
@1
@#
d 和 +2d 的区别
sed -r '/^adm/,20d' /etc/passwd # 删除到20行
sed -r '/^adm/,+20d' /etc/passwd # 再删除20行
修改类似路径的数据
若文件中有类似路径的数据需要修改
[root@wzb shell]# cat u.txt
/usr/local/nginx
尝试
无法识别
[root@wzb shell]# sed -r 's//usr/local//home/wzb/' u.txt
sed:-e 表达式 #1,字符 8:“s”的未知选项
换一个方式 就可以了
[root@wzb shell]# sed -r 's@/usr/local@/home/wzb@' u.txt
/home/wzb/nginx
n 获取下一行命令
这里发现若文件中有2个相关行,只会对靠后的行进行相关操作
sed -r '/adm/{n;s/sbin/uuu/}' #找到adm之后的第一个匹配行,进行替换
sed -r '/adm/{n;n;s/sbin/uuu/}' #找到adm之后的第二个匹配行,进行替换
删除注释行
[root@wzb practice]# cat u.txt
#123
#
#
123
123
123#123
操作
\t 表示tab
有0个或多个空格&有0个或多个tab
[root@wzb practice]# sed -r '/^[ \t]*#.*/d' u.txt
123
123
123#123
删除以//开头的行
[root@wzb practice]# sed '/^[ \t]*\/\//d' u.txt
123
123
123//123
指定位置添加#,已有的不再添加
[root@wzb practice]# cat u.txt
#123
#123
123
123
#123
#123
#
#
操作
[root@wzb practice]# sed -r 's/^[ \t#]*/#/' u.txt
#123
#123
#123
#123
#123
#123
#
#
在末尾添加数据、变量的使用
[root@wzb practice]# sed -r "\$a$name" u.txt
也可以
[root@wzb practice]# sed -r '$a'"$name" u.txt
123
123
wzb123
替换成自己+xx
[root@wzb applicaton_of_sed]# cat t1.txt
192.168.92.5
192.168.92.131
aa
bb
cc
11
22
33
192.168.92.5
192.168.92.5
192.168.92.5
192.168.92.9
答案
需要添加 -r 否则 \1无法被识别
[root@wzb applicaton_of_sed]# sed -r 's/(192.168.92.5)/\1localhost/g' t1.txt
192.168.92.5localhost
192.168.92.131
aa
bb
cc
11
22
33
192.168.92.5localhost
192.168.92.5localhost
192.168.92.5localhost
192.168.92.9
将内容替换为与之后面紧接的数字
如 digit33要被改为333
[root@wzb applicaton_of_sed]# cat t2.txt
aa digit3
digit44bb
12digitb
答案
[0-9]即为 0-9的遍历
[root@wzb applicaton_of_sed]# sed -r 's/digit([0-9])/\1\1/g' t2.txt
aa 33
444bb
12digitb
将所有的loverable替换成loves
[root@wzb applicaton_of_sed]# cat t3.txt
loverable
loverable123
lovera
love
loverable
loverable
loverable
答案
[root@wzb applicaton_of_sed]# sed -r 's/(lover)able/\1s/g' t3.txt
lovers
lovers123
lovera
love
lovers
lovers
lovers
打印奇数行或偶数行
[root@wzb applicaton_of_sed]# cat t4.txt
1
2
3
4
5
6
7
8
9
答案
意为从第一行开始,每两行输出一次,即为奇数行
那么偶数行就可以改为sed -n '2~2p' t4.txt
[root@wzb applicaton_of_sed]# sed -n '1~2p' t4.txt
1
3
5
7
9
当一行包含,就修改内容
当一行包含east…west就添加aaabbb
[root@wzb applicaton_of_sed]# cat t5.txt
east 123 123 123 west
east a b c west
east z z z wesz
east yes west yes
答案
[root@wzb applicaton_of_sed]# sed -r -n 's/(^east.*west.*$)/\1aaabbb/p' t5.txt
east 123 123 123 westaaabbb
east a b c westaaabbb
east yes west yesaaabbb
指定范围修改内容
让包含start
的行到包含end
的行之间的aa bb
替换成AA BB
[root@wzb applicaton_of_sed]# cat t6.txt
aa bb
start
aa bb
abc abc
aa bb
aabb
end
start
aabb
答案
/start/
表示与start相关的行
/start/,/end/
表示
打完与之相关行的命令后直接打s///
[root@wzb applicaton_of_sed]# sed '/start/,/end/s/aa bb/AA BB/g' t6.txt
aa bb
start
AA BB
abc abc
AA BB
aabb
end
start
aabb
输出33后的所有行
[root@wzb applicaton_of_sed]# cat t1.txt
192.168.92.5
192.168.92.131
aa
bb
cc
11
22
33
192.168.92.5
192.168.92.5
192.168.92.5
192.168.92.9
答案
$:直接到结尾
[root@wzb applicaton_of_sed]# sed -n '/33/,$p' t1.txt
33
192.168.92.5
192.168.92.5
192.168.92.5
192.168.92.9
输出33后两行
答案
[root@wzb applicaton_of_sed]# sed -n '/33/,$p' t1.txt | sed '1,3p' -n
33
192.168.92.5
192.168.92.5
不懂
[root@wzb applicaton_of_sed]# sed -n -r '/33/{N;N;p;q}' t1.txt
33
192.168.92.5
192.168.92.5