目 录CONTENT

文章目录

文本处理三剑客

简中仙
2023-02-16 / 0 评论 / 0 点赞 / 104 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2023-10-07,若内容或图片失效,请留言反馈。 本文如有错误或者侵权的地方,欢迎您批评指正!

grep 命令主要对文本的(正则表达式)行基于模式进行过滤

sed:stream editor,文本编辑工具

awk:Linux上的实现gawk,文本报告生成器

一、文本处理三剑客之 grep

grep: Global search REgular expression and Print out the line

作用:文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查;打印匹配到的行

模式:由正则表达式字符及文本字符所编写的过滤条件

格式:

grep [OPTIONS] PATTERN [FILE...]
选项说明
--color=auto对匹配到的文本着色显示
-m #匹配#次后停止
-v显示不被pattern匹配到的行
-i忽略字符大小写
-n显示匹配的行号
-c统计匹配的行数
-o仅显示匹配到的字符串
-q静默模式,不输出任何信息
-A # after后#行
-B # before前#行
-C # context前后各#行
-e实现多个选项间的逻辑or关系,如:grep –e ‘cat ’ -e ‘dog’ file
-w匹配整个单词
-E使用ERE,相当于egrep
-F相当于fgrep,不支持正则表达式
-ffile 根据模式文件处理
-r递归目录,但不处理软链接
-R递归目录,但处理软链接

规则表达式

^    # 锚定行的开始 如:'^grep'匹配所有以grep开头的行。
$    # 锚定行的结束 如:'grep$' 匹配所有以grep结尾的行。
.    # 匹配一个非换行符的字符 如:'gr.p'匹配gr后接一个任意字符,然后是p。
*    # 匹配零个或多个先前字符 如:'*grep'匹配所有一个或多个空格后紧跟grep的行。
.*   # 一起用代表任意字符。
[]   # 匹配一个指定范围内的字符,如'[Gg]rep'匹配Grep和grep。
[^]  # 匹配一个不在指定范围内的字符,如:'[^A-FH-Z]rep'匹配不包含A-R和T-Z的一个字母开头,紧跟rep的行。
\(..\)  # 标记匹配字符,如'\(love\)',love被标记为1。
\<      # 锚定单词的开始,如:'\<grep'匹配包含以grep开头的单词的行。
\>      # 锚定单词的结束,如'grep\>'匹配包含以grep结尾的单词的行。
x\{m\}  # 重复字符x,m次,如:'0\{5\}'匹配包含5个o的行。
x\{m,\}   # 重复字符x,至少m次,如:'o\{5,\}'匹配至少有5个o的行。
x\{m,n\}  # 重复字符x,至少m次,不多于n次,如:'o\{5,10\}'匹配5--10个o的行。
\w    # 匹配文字和数字字符,也就是[A-Za-z0-9],如:'G\w*p'匹配以G后跟零个或多个文字或数字字符,然后是p。
\W    # \w的反置形式,匹配一个或多个非单词字符,如点号句号等。
\b    # 单词锁定符,如: '\bgrep\b'只匹配grep。

范例:

1、统计某文件夹下文件的个数

ls -l /data|grep "^-"|wc -l   #不包含子目录
ls -lR /data|grep "^-"|wc -l  #不含子目录

2、统计某文件夹下目录的个数

ls -l /data |grep "^d"|wc -l  #不包含子目录
ls -lR /data|grep "^d"|wc -l  #包含子目录

3、统计某目录(包含子目录)下的所有某种类型的文件

ls -lR /data|grep txt|wc -l

4、去除文本中的空行

grep -v '^\s*$' test.txt

5、多关键词匹配

grep pattern1 | pattern2 files :显示匹配 pattern1 或 pattern2 的 

grep pattern1 files | grep pattern2 :显示既匹配 pattern1 又匹配 pattern2 的行。

grep -E '关键词1|关键词2' 

6、xargs配合grep查找

find -type f -name '*.php'|xargs grep 'GroupRecord'

7、查找路径下含有某字符串的所有文件

grep -rn "hello,world!" *

8、完全匹配关键词

grep -Fx 关键词

9、精准匹配

grep -w 关键词 
# 不加-w是默认情况

10、显示文件非#注释内容

# 显示文件非#注释内容
grep -v ^[[:space:]]*# 文本文件

# # 显示文件非#注释内容,不显示空行
egrep -v '#|^$' filename

11、删除空行并重定向到新文件

grep . filename > newfilename

二、文本处理三剑客之 sed

1、sed 工作原理

sed 即 stream EDitor,和 vi 不同,sed是行编辑器

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。每当处理一行时,把当前处理的行存储在临时缓冲区中,称为模式空间(Pattern Space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。如果使用vi命令打开几十M上百M的文件,明显会出现有卡顿的现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开。Sed就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快

参考网站:https://www.gnu.org/software/sed/manual/sed.html

2、sed 基本用法

格式:

sed [-hnV][-e<script>][-f<script文件>][文本文件]
选项说明
-n或 --quiet或--silent仅显示script处理后的结果
-e <script> 或 --expression=<script>以选项中指定的script来处理输入的文本文件
-f <script文件> 或 --file=<script文件>以选项中指定的script文件来处理输入的文本文件
-r, -E使用扩展正则表达式
-i.bak备份文件并原处编辑
-h 或 --help显示帮助

动作说明

  • a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
  • c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
  • d :删除,因为是删除啊,所以 d 后面通常不接任何东西;
  • i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
  • p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行
  • s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g

1、替换环境变量的值到文件中

如果想替换字符串中的值为环境变量的值,可以使用将匹配规则使用""括起来,在""中直接引用环境变量

echo "test asdasdas \$TEST" > test.txt
export aaaaa=1234
sed -i -e "s/\$TEST/$aaaaa/g" test.txt

如果变量的值中包含特殊字符,例如/,与sed匹配模式的关键字符造成歧义。可使用代替/

echo "test asdasdas \$TEST" > test.txt
export aaaaa=/1234
sed -i "s?\$TEST?$aaaaa?g" test.txt

2、新增内容到末尾行的末尾

sed '$ s/$/新增内容/'  file_path

3、去除文本中空行和开头"##"的行

sed '/^$/d;/^##/d'  file_path

4、去除文本中的空行

sed '/^\s*$/d' test.txt

5、多个匹配规则

sed -i -e '/hah/a lala\nhehe' -e '/lala/d' test

6、在查找到匹配行后的操作

sed -i '/hah/a lallalla' test   #在查找到匹配行后添加一行
sed -i '/hah/a lala\nhehe' test #在查找到匹配行后添加多行
sed -i '/hah/d' test #删除查找到匹配行

7、在查找匹配行的末首或末尾添加内容

sed -i '/ha/ s/^/la' test #在查找包含"ha"的行首追加"la","laha"
sed -i '/ha/ s/$/la' test #在查找包含"ha"的行末追加"la","hala"

8、去掉文本中开头带#号注释的行

sed -i -c -e '/^$/d;/^#/d'  file

9、去除文本中的换行符^M

Windows下保存的文本文件,上传到Linux/Unix下后总会在末尾多了一个换行符^M,导致一些xml、ini、sh等文件读取错误

sed 's/^M//' 原文件>新文件

# 注意,^M = Ctrl v + Ctrl m,而不是手动输入^M

10、每行前后添加空行

每行后面添加一行空行:

sed G tmp

每行前面添加一行空行:

sed '{x;p;x;}' tmp

每行后面添加两行空行:

sed 'G;G' tmp

每行前面添加两行空行:

sed '{x;p;x;x;p;x;}' tmp

每行后面添加三行空行:

sed 'G;G;G' tmp

每行前面添加三行空行:

sed '{x;p;x;x;p;x;x;p;x}' tmp

依次类推,添加几行空行,就有几个G或者x;p;x

11、如果行后有空行,则删除,然后每行后面添加空行

sed '/^$/d;G' tmp

12、在匹配行前后添加空行

如果一行里面有shui这个单词,那么在他后面会添加一个空行

sed '/shui/G' tmp 

如果一行里面有shui这个单词,那么在他前后各添加一个空行

sed '/shui/{x;p;x;G}' tmp

如果一行里面有shui这个单词,那么在他前面添加一个空行

sed '/shui/{x;p;x;}' tmp 

在第一行前面添加空行,想在第几行,命令中的1就改成几

sed '1{x;p;x;}' tmp 

在第一行后面添加空行,想在第几行,命令中的1就改成几

sed '1G' tmp 

13、每几行后面添加一个空行

每两行后面增加一个空行

 sed 'N;/^$/d;G' file.txt

每两行前面添加一个空行

 sed 'N;/^$/d;{x;p;x;}' tmp

每三行后面增加一个空行

 sed 'N;N;/^$/d;G' file.txt

每三行前面增加一个空行

 sed 'N;N;/^$/d;{x;p;x;}' tmp

14、以x为开头或以x为结尾的行前后添加空行

以xi为开头的行后面添加空行

 sed '/^xi/G;' tmp

以xi为结尾的行前面添加空行

 sed '/^xi/{x;p;x;}' tmp

以xi为结尾的行后面添加空行

 sed '/xi$/G;' tmp

以xi为结尾的行后面添加空行

 sed '/xi$/{x;p;x;}' tmp

三、文本处理三剑客之 awk

1、awk 工作原理和基本用法说明

awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会(FSF)进行开发和维护,通常也称它为 GNU AWK

有多种版本:

AWK:原先来源于 AT & T 实验室的的AWK

NAWK:New awk,AT & T 实验室的AWK的升级版

GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容

gawk:模式扫描和处理语言,可以实现下面功能

  • 文本处理
  • 输出格式化的文本报表
  • 执行算数运算
  • 执行字符串操作

格式:

awk [options]   'program'   var=value   file…
awk [options]   -f programfile    var=value  file…

说明:

program通常是被放在单引号中,并可以由三种部分组成

  • BEGIN语句块
  • 模式匹配的通用语句块
  • END语句块

常见选项:

  • -F “分隔符” 指明输入时用到的字段分隔符
  • -v var=value 变量赋值

Program格式:

pattern{action statements;..}

pattern:决定动作语句何时触发及触发事件,比如:BEGIN,END

action statements:对数据进行处理,放在{}内指明,常见:print, printf

awk 工作过程

第一步:执行BEGIN{action;… }语句块中的语句

第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。

第三步:当读至输入流末尾时,执行END{action;…}语句块

BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块 pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

分割符、域和记录

  • 由分隔符分隔的字段(列column,域field)标记1,2...n称为域标识,0为所有域,注意:和shell中变量$符含义不同
  • 文件的每一行称为记录record
  • 如果省略action,则默认执行 print $0 的操作

常用的action分类

  • output statements:print,printf
  • Expressions:算术,比较表达式等
  • Compound statements:组合语句
  • Control statements:if, while等
  • input statements

awk控制语句

  • { statements;… } 组合语句
  • if(condition) {statements;…}
  • if(condition) {statements;…} else {statements;…}
  • while(conditon) {statments;…}
  • do {statements;…} while(condition)
  • for(expr1;expr2;expr3) {statements;…}
  • break
  • continue
  • exit

2、动作 print

格式:

print item1, item2, ...

说明:

  • 逗号分隔符
  • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
  • 如省略item,相当于print $0

范例:

# awk    '{print "hello,awk"}'
# awk -F: '{print "wang"}' /etc/passwd
# awk -F: '{print}' /etc/passwd
# awk -F: '{print $0}' /etc/passwd
# awk -F: '{print $1,$3}' /etc/passwd
# awk -F: '{print $1"\t"$3}' /etc/passwd
# grep "^UUID" /etc/fstab |awk {'print $2,$3'}
/ ext4
# awk '{print $6}' access.log |sort |uniq -c|sort -nr|head
    523 "171.117.45.158",
     36 "106.38.223.178",
     35 "47.92.36.178",
     20 "164.92.189.255",
     20 "159.203.44.43",
     19 "159.223.236.123",
     19 "139.59.138.104",
     14 "47.97.211.99",
     14 "47.92.29.136",
     13 "125.124.222.109",
# df | awk -F"[[:space:]]+|%" '{print $5}'
Use
0
0
1
0
5
1
92
1
# df | grep "^/dev/sd" | awk -F"[[:space:]]+|%" '{print $5}'
5
1
92
# df |  awk -F"[[:space:]]+|%" '/^\/dev\/sd/{print $5}'
5
1
92

3、awk变量

awk中的变量分为:内置和自定义变量

常见的内置变量

FS:输入字段分隔符,默认为空白字符,功能相当于 -F

范例:

awk -v FS=':'  '{print $1,FS,$3}' /etc/passwd
awk -v FS=":" '{print $1FS$3}' /etc/passwd
awk –F:   '{print $1,$3,$7}'   /etc/passwd  
S=:;awk -v FS=$S '{print $1FS$3}' /etc/passwd

OFS:输出字段分隔符,默认为空白字符

范例:

awk  -v FS=':'  -v OFS=':'  '{print $1,$3,$7}'   /etc/passwd

RS:输入记录分隔符,指定输入时的换行符

范例:

awk -v RS=' ' ‘{print }’ /etc/passwd

ORS:输出记录分隔符,输出时用指定符号代替换行符

范例:

awk -v RS=' ' -v ORS='###'  '{print $0}' /etc/passwd

NF:字段数量

范例:

#引用变量时,变量前不需加$
# awk  -F:'{print NF}'   /etc/fstab  
# awk  -F:'{print $(NF-1)}'  /etc/passwd
# ls /misc/cd/BaseOS/Packages/*.rpm |awk -F"." '{print $(NF-
1)}'|sort |uniq -c
    389 i686
    208 noarch
   1060 x86_64
# ss -nt |grep "^ESTAB" | awk -F"[[:space:]]+|:" '{print $(NF-2)}'
10.0.0.1
10.0.0.7
10.0.0.1
# ss -nt |awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'

范例: 将连接数超过100个以上的IP放入黑名单拒绝访问

cat deny_dos.sh

LINK=100
while true;do
    ss -nt | awk -F"[[:space:]]+|:" '/^ESTAB/{print $(NF-2)}'|sort |uniq -
c|while read count ip;do
        if [ $count -gt $LINK ];then
            iptables -A INPUT -s $ip -j REJECT
        fi
    done
done

NR:记录编号

范例:

# awk -F: '{print NR}' /etc/passwd
1
2
3
.......
# awk -F: 'END{print NR}' /etc/passwd
57
# awk -F: 'BEGIN{print NR}' /etc/passwd
0

FNR:各文件分别计数,记录号

范例:

awk '{print FNR}'  /etc/fstab /etc/inittab
# awk  '{print NR,$0}' /etc/issue /etc/redhat-release 
1 \S
2 Kernel \r on an \m
3 
4 CentOS Linux release 8.0.1905 (Core) 
# awk  '{print FNR,$0}' /etc/issue /etc/redhat-release 
1 \S
2 Kernel \r on an \m
3 
1 CentOS Linux release 8.0.1905 (Core)

FILENAME:当前文件名

范例:

# awk '{print FILENAME}'  /etc/fstab
# awk  '{print FNR,FILENAME,$0}' /etc/issue /etc/redhat-release 
1 /etc/issue \S
2 /etc/issue Kernel \r on an \m
3 /etc/issue 
1 /etc/redhat-release CentOS Linux release 8.0.1905 (Core)

ARGC:命令行参数的个数

范例:

# awk '{print ARGC}'  /etc/issue /etc/redhat-release 
3
3
3
3
# awk 'BEGIN{print ARGC}'  /etc/issue /etc/redhat-release 
3

ARGV:数组,保存的是命令行所给定的各参数

范例:

# awk 'BEGIN{print ARGV[0]}'  /etc/issue /etc/redhat-release 
awk
# awk 'BEGIN{print ARGV[1]}'  /etc/issue /etc/redhat-release 
/etc/issue
# awk 'BEGIN{print ARGV[2]}'  /etc/issue /etc/redhat-release 
/etc/redhat-release
# awk 'BEGIN{print ARGV[3]}'  /etc/issue /etc/redhat-release

自定义变量(区分字符大小写)

  • -v var=value
  • 在program中直接定义

范例:

awk  -v test='hello gawk' '{print test}' /etc/fstab 
awk  -v test='hello gawk' 'BEGIN{print test}' 
awk  'BEGIN{test="hello,gawk";print test}' 
awk  -F: '{sex=“male”;print $1,sex,age;age=18}' /etc/passwd
cat awkscript
{print script,$1,$2}
awk  -F: -f awkscript script="awk" /etc/passwd

4、动作 printf

printf 可以实现格式化输出

格式:

printf “FORMAT”, item1, item2, ...

说明:

  • 必须指定FORMAT
  • 不会自动换行,需要显式给出换行控制符,\n
  • FORMAT中需要分别为后面每个item指定格式符

格式符:与item一一对应

  • %c:显示字符的ASCII码
  • %d, %i:显示十进制整数
  • %e, %E:显示科学计数法数值
  • %f:显示为浮点数
  • %g, %G:以科学计数法或浮点形式显示数值
  • %s:显示字符串
  • %u:无符号整数
  • %%:显示%自身

修饰符

#[.#]   第一个数字控制显示的宽度;第二个#表示小数点后精度,如:%3.1f
-       左对齐(默认右对齐) 如:%-15s
+       显示数值的正负符号   如:%+d

范例:

awk -F:   '{printf "%s",$1}' /etc/passwd
awk -F:   '{printf "%s\n",$1}' /etc/passwd
awk -F:   '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F:   '{printf "Username: %s\n",$1}'  /etc/passwd
awk -F:   '{printf “Username: %sUID:%d\n",$1,$3}'   /etc/passwd
awk -F:   '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd
awk -F:   '{printf "Username: %-25sUID:%d\n",$1,$3}'    /etc/passwd

5、操作符

算术操作符:

x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为负数
+x:将字符串转换为数值

字符串操作符:没有符号的操作符,字符串连接

赋值操作符:

=, +=, -=, *=, /=, %=, ^=,++, --

范例:

# awk  'BEGIN{i=0;print ++i,i}'
1 1
# awk  'BEGIN{i=0;print i++,i}'
0 1

比较操作符:

==, !=, >, >=, <, <=

模式匹配符:

~   左边是否和右边匹配,包含关系
!~  是否不匹配

范例:

# awk  -F: '$0 ~ /root/{print $1}'  /etc/passwd
# awk  -F: '$0 ~ "^root"{print $1}'  /etc/passwd
# awk  '$0  !~ /root/'   /etc/passwd
# awk  '/root/'   /etc/passwd
# awk  -F: '$3==0'     /etc/passwd
# df |  awk -F"[[:space:]]+|%" '$0 ~ /^\/dev\/sd/{print $5}'
5
1
92
# ifconfig  eth0 | awk 'NR==2{print $2}'
10.0.0.8

逻辑操作符:

与&&,或||,非!

范例:

awk -F:   '$3>=0 && $3<=1000 {print $1}'  /etc/passwd
awk -F:   '$3==0 || $3>=1000 {print $1}'  /etc/passwd 
awk -F:   '!($3==0) {print $1}'     /etc/passwd
awk -F:   '!($3>=500) {print $3}' /etc/passwd

条件表达式(三目表达式)

selector?if-true-expression:if-false-expression

范例:

awk -F: '{$3>=1000?usertype="Common User":usertype="SysUser";printf "%-20s:%12s\n",$1,usertype}'   /etc/passwd

6、模式PATTERN

PATTERN:根据pattern条件,过滤匹配的行,再做处理

1、如果未指定:空模式,匹配每一行

范例:

# awk -F: '{print $1,$3}' /etc/passwd

2、/regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来

范例:

# awk   '/^UUID/{print $1}'     /etc/fstab
# awk   '!/^UUID/{print $1}'   /etc/fstab
# df  | awk '/^\/dev\/sd/'
/dev/sda2      104806400 4935924  99870476   5% /
/dev/sda3       52403200  398876  52004324   1% /data
/dev/sda1         999320  848572     81936  92% /boot

3、relational expression: 关系表达式,结果为“真”才会被处理

真:结果为非0值,非空字符串

假:结果为空字符串或0值

范例:

awk   -F:  'i=1;j=1{print i,j}' /etc/passwd
awk  '!0'  /etc/passwd ;awk  '!1'   /etc/passwd
awk  -F: '$3>=1000{print $1,$3}'  /etc/passwd
awk  -F: '$3<1000{print $1,$3}'  /etc/passwd
awk  -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
awk  -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd

4、line ranges:行范围

/pat1/,/pat2/ 不支持直接给出数字格式

范例:

# awk '/^bin/,/^adm/' /etc/passwd
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
# sed  -n '/^bin/,/^adm/p' /etc/passwd
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
# awk 'NR>=3 && NR<=6{print NR,$0}' /etc/passwd
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
6 sync:x:5:0:sync:/sbin:/bin/sync
# sed -n '3,6p' /etc/passwd
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

5、BEGIN/END模式

BEGIN{}:仅在开始处理文件中的文本之前执行一次

END{}:仅在文本处理完成之后执行一次

范例

awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3} END{print "END FILE"}' 
/etc/passwd
awk -F: '{print "USER USERID";print $1":"$3} END{print "END FILE"}'  /etc/passwd
awk -F: 'BEGIN{print "USER  UID  \n--------------- "}{print $1,$3}'  /etc/passwd
awk -F: 'BEGIN{print "USER UID  \n------"}{print $1,$3}'END{print "========"} 
/etc/passwd
seq 10 | awk   'i=0'
seq 10 | awk   'i=1'
seq 10 | awk   'i=!i'
seq 10 | awk   '{i=!i;print i}'
seq 10 | awk   '!(i=!i)'              
seq 10 | awk  -v  i=1 'i=!i'

7、条件判断 if-else

语法:

if(condition){statement;…}[else statement]
if(condition1){statement1}else if(condition2){statement2}else{statement3}

使用场景:对awk取得的整行或某个字段做条件判断

范例:

awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or Sysuser: %s\n",$1}' /etc/passwd
df -h|awk -F% '/^\/dev\/sd/{print $1}'| awk '$NF>=80{print $1,$5}'
df | awk -F"[[:space:]]+|%" '/^\/dev\/sd/{if($5>80)print $1,$5}'
awk 'BEGIN{ test=100;if(test>90){print "very good"}
    else if(test>60){ print "good"}else{print "no pass"}}'

8、switch语句

语法:

switch(expression) {case VALUE1 or /REGEXP/: statement1; case VALUE2 or /REGEXP2/: statement2; ...; default: statementn}

9、循环while

语法:

while (condition) {statement;…}

条件“真”,进入循环;条件“假”,退出循环

使用场景:

  • 对一行内的多个字段逐一类似处理时使用
  • 对数组中的各元素逐一处理时使用

示例:

# awk 'BEGIN{print length("hello")}'
5
# awk 'BEGIN{print length("马哥教育")}'
4
# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print 
$i,length($i); i++}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
# awk  '/^[[:space:]]*linux16/{i=1;while(i<=NF) 
{if(length($i)>=10){print $i,length($i)}; i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
crashkernel=auto 16
net.ifnames=0 13
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
crashkernel=auto 16
net.ifnames=0 13
# awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print 
total}'
5050

10、循环 do-while

语法:

do {statement;…}while(condition)

意义:无论真假,至少执行一次循环体

do-while循环

语法:do {statement;…}while(condition)

意义:无论真假,至少执行一次循环体

范例:

# awk 'BEGIN{ total=0;i=1;do{ total+=i;i++;}while(i<=100);print total}'
5050

11、循环for

语法:

for(expr1;expr2;expr3) {statement;…}

常见用法:

for(variable assignment;condition;iteration process) {for-body}

特殊用法:能够遍历数组中的元素

for(var in array) {for-body}

范例:

# awk 'BEGIN{total=0;for(i=1;i<=100;i++){total+=i};print total}'
5050

范例:

# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print 
$i,length($i)}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-1062.el7.x86_64 31
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-0-rescue-b12558570741487c9328c996e3265b09 50
root=UUID=bebb9244-bbb8-4c69-9249-54a36c75155e 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
net.ifnames=0 13

性能比较

time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
time (total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
time (for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
time (seq –s ”+” 10000|bc)

12、continue和break

格式:

continue [n]
break [n]

范例:

# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
2500
# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i}print sum}'
1225

13、next

next 可以提前结束对本行处理而直接进入下一行处理(awk自身循环)

范例:

# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
ftp 14
nobody 65534
polkitd 998
gluster 996
rtkit 172
rpc 32
chrony 994
saslauth 992
clevis 984
pegasus 66
colord 982
setroubleshoot 980
gdm 42
gnome-initial-setup 978
sshd 74
avahi 70
tcpdump 72
wang 1000

14、数组

awk的数组为关联数组

格式

array[index-expression]

范例:

weekdays["mon"]="Monday"

index-expression

  • 可使用任意字符串;字符串要使用双引号括起来
  • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
  • 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历

范例:

# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday

范例:

awk '!line[$0]++'  dupfile
awk '{!line[$0]++;print $0, line[$0]}'  dupfile

范例:判断数组索引是否存在

# awk 'BEGIN{array["i"]="x"; array["j"]="y" ; print "i" in array, "y" in array }'
1 0
# awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("i" in array ) {print "存在"}else{print "不存在"}}'
存在
# awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("abc" in array ) {print "存在"}else{print "不存在"}}'
不存在

若要遍历数组中的每个元素,要使用for循环

for(var in array) {for-body}

注意:var会遍历array的每个索引

范例:遍历数组

# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
Tuesday
Monday
# awk 'BEGIN {
a["x"] = "welcome"
a["y"] = "to"
a["z"] = "Magedu"
for (i in a) {
     print i,a[i]
}
}'
x welcome
y to
z Magedu
# awk -F: '{user[$1]=$3}END{for(i in user){print "username: "i,"uid: "user[i]}}' /etc/passwd
username: adm uid: 3
username: rpc uid: 32
username: dnsmasq uid: 985
username: radvd uid: 75
username: sync uid: 5
username: mail uid: 8
username: exim uid: 93
username: tss uid: 59
username: gluster uid: 996
username: unbound uid: 995
username: halt uid: 7

范例:

# cat  ss.log | sed -nr '1!s/^([^0-9]+) .*/\1/p'|sort |uniq -c
    529 ESTAB    
      9 LISTEN   
    128 SYN-RECV 
     95 TIME-WAIT
# ss -ant | awk 'NR!=1{state[$1]++}END{for(i in state){print i,state[i]}}'
SYN-RECV 128
LISTEN 9
ESTAB 529
TIME-WAIT 95
# netstat -tan | awk '/^tcp/{state[$NF]++}END{for(i in state){print i,state[i]}}'
LISTEN 9
SYN_RECV 126
ESTABLISHED 523
FIN_WAIT2 40
# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
172.20.0.200 1482
172.20.21.121 2
172.20.30.91 29
172.16.102.29 864
172.20.0.76 1565
172.20.9.9 15
172.20.1.125 463
172.20.61.11 2
172.20.73.73 198
# awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access_log  |sort -nr| head -3
4870 172.20.116.228
3429 172.20.116.208
2834 172.20.0.222
# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log |sort -k2 -nr|head -3
172.20.116.228 4870
172.20.116.208 3429
172.20.0.222 2834

范例:多维数组

# awk 'BEGIN{
> array[1][1]=11
> array[1][2]=12
> array[1][3]=13
> array[2][1]=21
> array[2][2]=22
> array[2][3]=23
> for (i in array)
>     for (j in array[i])
>         print array[i][j]
> }'
11
12
13
21
22
23

15、awk脚本

将awk程序写成脚本,直接调用或执行

范例:

# cat passwd.awk 
{if($3>=1000)print $1,$3}
# awk -F: -f passwd.awk /etc/passwd
nobody 65534
wang 1000
mage 1001

范例:

# cat test.awk
#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
# chmod +x test.awk
# ./test.awk -F: /etc/passwd
nobody 65534
wang 1000
mage 1001

向awk脚本传递参数

格式:

awkfile  var=value var2=value2... Inputfile

注意:在BEGIN过程中不可用。直到首行输入完成以后,变量才可用。可以通过-v 参数,让awk在执行

BEGIN之前得到变量的值。命令行中每一个指定的变量都需要一个-v参数

范例:

# cat test2.awk 
#!/bin/awk -f
{if($3 >=min && $3<=max)print $1,$3} 
# chmod +x test2.awk
# ./test2.awk  -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
abrt 173

获取匹配关键字后的内容

awk '{ if(match($0,"关键字")) {print substr($0,RSTART+RLENGTH) }}'文件

#示例

    # 原始文本
    2018-07-31T09:33:08.160102Z 1 [Note] A temporary password isgenerated for root@localhost: oco4Pr&a!o;v

    # 命令
    awk '{ if(match($0,"root@localhost: ")) {print substr($0,RSTAR+RLENGTH) }}' test.log

    # 结果
    oco4Pr&a!o;v

去除文本中的空行

awk NF test.txt

# NF代表当前行的字段数,空行的话字段数为0,被awk解释为假,因此不进行输出。

获取匹配关键字后多少的位字符串

# 样本
a="Location: https://allinone.okd311.curiouser.com:8443/oauth/token/implicit#access_token=FBHwgR1jj2coLoYYfG9SdGUke9L9HmAU2IOI9GaMKrQ&expires_in=86400&scope=user%3Afull&token_type=Bearer"

# 获取"access_token="的值

# 方式一

    echo $a | grep "access_token=" |awk -F"access_token=" '/access_token=/{printf substr($2,0,43)}'

    # 结果: FBHwgR1jj2coLoYYfG9SdGUke9L9HmAU2IOI9GaMKrQ

# 方式二

    echo $a | grep "access_token=" |awk '{ if(match($0,"access_token=")) {print substr($0,RSTART+RLENGTH) }}'| awk -F '&' '{print $1}'

    # 结果: FBHwgR1jj2coLoYYfG9SdGUke9L9HmAU2IOI9GaMKrQ

使用多个分隔符分割字符串

# @, 空格和Tab都是字段分隔符
awk -F”[@ /t]" '{print $1 $2}'

为每行增加行号

awk '$0=NR":"$0' 文件名 > 新文件名

# $0表示原来每行的内容,
# NR表示行号,
# 双引号之间表示行号与原来内容之间的delimiter
0

评论区