栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

深入浅出认识awk

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

深入浅出认识awk

概述

awk是Unix-like系统常用的文本处理工具。

其功能可总结为:将输入文本切成表格,通过行筛选、字段重组、跨行上下文、逻辑判断等灵活组合,实现定制化输出。

简单示例

通过简单示例,建立初步印象。
输入文件:coord_list

115.631449,33.110324;115.638022,33.116035
0.0,0.0;0.0,0.0
115.150937,40.549179;115.157529,40.554885
110.029599,27.578549;110.036011,27.584827
0.0,0.0;0.0,0.0

命令:

awk -F'[,;]' '$1!=0 {print $3"|"$4}' coord_list

其中

参数: -F'[,;]' 指定正则做为字段分隔符(单个逗号或分号);条件: $1!=0 第一个字段必须不等于0;动作: {print $3"|"$4} 输出第三和第四个字段,中间以竖线分隔。

输出:

115.638022|33.116035
115.157529|40.554885
110.036011|27.584827
命令结构

参数部分

常用的有

-F 指定字段切分的分隔符,默认为空格。可以指定单个字符,比如 -F, 或 -F: 或 -F't' ;如果指定多个字符,则视为正则表达式,比如 -F' |t' 或 -F's+' 脚本部分

完整形态的脚本模式:

'BEGIN{前置动作}  行操作  END{后置动作}'

其中

BEGIN{前置动作}:可选,在整个文件处理开始前执行,一般会 修改配置、定义全局变量、打印表头等行操作(for each line):可选,每行数据执行一次,具体模式见下文END{后置动作}:可选,在整个文件处理结束后执行,一般会 处理Buffer中的残留内容

行操作的具体模式有

'条件':对满足条件的行,执行隐含动作:直接打印当前行(等价于 '条件 {print $0}' )'{动作}':对所有行,执行动作'条件 {动作}':对满足条件的行,执行动作'条件1 {动作1} 条件2 {动作2}':对满足条件1的行,执行动作1;对满足条件2的行,执行动作2 脚本使用案例 案例1:包含完整命令结构的简单示例【对前5个数字求平均】

输入文件:top10

美国,72958690
印度,39799202
巴西,24134946
法国,16800913
英国,15953685
俄罗斯,11241109
土耳其,11014152
意大利,10001344
西班牙,9280890
德国,8808107

命令:

awk -F, 'BEGIN{sum=0;cnt=0} NR<=5 {sum+=$2;cnt++} END{print sum/cnt/10000"万"}' top10

其中

NR<=5:筛选前5行。NR为awk内置变量,含义为当前行号(Number of Records)

输出:

3392.95万
案例2:使用全局变量的简单示例【构造进程pid关系链】

输入文件:ps(来自 ps -ef 输出结果,已脱敏)

UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 Jan17 ?        00:36:47 /usr/bin/python /usr/lib/systemd/syste+
root        444      1  0 Jan17 ?        00:00:08 /usr/sbin/sshd -D
root        448      1  0 Jan17 ?        00:00:05 /usr/sbin/crond -n
root        486      1  0 Jan17 ?        01:19:11 /home/staragent/bin/staragentd
root        880      0  0 Jan17 ?        00:33:10 /home/a/xxxxxxxxxx/xxxxxxxxxx -conf /h+
root        924    880  0 Jan17 ?        00:00:00 [nslookup] 
root       2491      1  0 Jan17 ?        00:00:00 /usr/local/ilogtail/ilogtail
root       2493   2491  0 Jan17 ?        00:43:06 /usr/local/ilogtail/ilogtail
root       4430      0  0 Jan17 ?        00:00:00 nginx: master process /opt/xxxxxx/xxxx+
root      16803      0  0 11:41 ?        00:00:03 /usr/bin/xxxxxxxxxx --verbose=2 -f /us+
root      63148      1  0 Jan19 ?        00:15:10 /home/staragent/plugins/xxxxxxxxxxxx/j+
root      85570    444  0 17:26 ?        00:00:00 sshd: yizishou [priv]
yizishou  85572  85570  0 17:26 ?        00:00:00 sshd: yizishou@pts/0
yizishou  85573  85572  0 17:26 pts/0    00:00:00 -bash
yizishou  90169  85573  0 17:50 pts/0    00:00:00 ps -ef
root     200642      1  0 Jan18 ?        00:00:35 /usr/sbin/syslog-ng -F -p /var/run/sys+
root     200654      1  0 Jan18 ?        00:02:43 /usr/lib/systemd/systemd-journald
root     200963      1  1 Jan19 ?        01:36:22 /home/xxxxxxxxx/plugins/xxxxxxxxx/xxxx+

命令:

awk 'NR>1 {s=(m[$3]==null?$3:m[$3])"-"$2; m[$2]=s; print s}' ps

其中

NR>1:过滤掉表头行。NR为awk内置变量,含义为当前行号(Number of Records)m:awk的变量是免定义的,会根据用法自动推断类型并初始化。这里将m作为Map使用,初始化为空Map

输出:

0-1
0-1-444
0-1-448
0-1-486
0-880
0-880-924
0-1-2491
0-1-2491-2493
0-4430
0-16803
0-1-63148
0-1-444-85570
0-1-444-85570-85572
0-1-444-85570-85572-85573
0-1-444-85570-85572-85573-90169
0-1-200642
0-1-200654
0-1-200963
案例3:跨行合并与还原的案例【Java栈整体筛选】

思路:将多行合并为一行,筛选后还原为多行
输入文件:stk(来自 jstack 输出结果,截取部分内容)

2022-01-25 11:18:57
Full thread dump OpenJDK 64-Bit Server VM (25.242-b24 mixed mode):

"process reaper" #38398 daemon prio=10 os_prio=0 tid=0x00002b4b4e019800 nid=0x256b6 waiting on condition [0x00002b4b2af7c000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park0(Native Method)
        - parking to wait for  <0x00000000f820d960> (a java.util.concurrent.SynchronousQueue$TransferStack)
        at sun.misc.Unsafe.park(Unsafe.java:1038)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:216)
        at ...

"Attach Listener" #479 daemon prio=9 os_prio=0 tid=0x00002b4b49613000 nid=0x1430f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

...

"Surrogate Locker Thread (Concurrent GC)" #4 daemon prio=9 os_prio=0 tid=0x00002b4b2b04a000 nid=0xf6c7 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00002b4b2b049000 nid=0xf6c6 in Object.wait() [0x00002b4b426ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000f8038ed8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:287)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00002b4b2b048000 nid=0xf6c5 in Object.wait() [0x00002b4b421d4000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000f8038f08> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"main" #1 prio=5 os_prio=0 tid=0x00002b4b2b047000 nid=0xf6b8 in Object.wait() [0x00002b4b283b7000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000f803ca80> (a io.netty.channel.AbstractChannel$CloseFuture)
        at java.lang.Object.wait(Object.java:502)
        at io.netty.util.concurrent.DefaultPromise.await(DefaultPromise.java:254)
        - locked <0x00000000f803ca80> (a io.netty.channel.AbstractChannel$CloseFuture)
        at io.netty.channel.DefaultChannelPromise.await(DefaultChannelPromise.java:129)
        at ...
...

命令:

awk '{ if(match($0,"^\s")==1) {s=s"@@"$0} else { if(s!="") {print s} s=$0 } } END{if(s!="") {print s}}' stk 
| grep 'java.lang.ref.Reference' 
| awk 'BEGIN{RS="n|@@"} {print $0}'

其中

前一句awk:将Java栈压缩为一行(以@@分隔,以便还原)

if(match($0,"^\s")==1):判断当前行是否以空白字符开头。如果true,则认为是跟随行,需要合并到上一行后面s:awk的变量是免定义的,会根据用法自动推断类型并初始化。这里将s作为String使用,初始化为空串s=s"@@"$0:将当前行拼接在变量s后面,以@@分隔 grep语句:在压缩的Java栈中筛选"java.lang.ref.Reference"后一句awk:将压缩的Java栈还原为多行

RS:是awk的内置变量,即行分隔符(Record Separator),默认为换行符nBEGIN{RS="n|@@"}:将行分隔符修改为正则表达式n|@@。即保留默认n的基础上,追认@@为换行符

输出:内容包含 java.lang.ref.Reference 的两个线程栈

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00002b4b2b049000 nid=0xf6c6 in Object.wait() [0x00002b4b426ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000f8038ed8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:287)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00002b4b2b048000 nid=0xf6c5 in Object.wait() [0x00002b4b421d4000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000f8038f08> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
案例4:多文件关联的案例【维度文件(小)与数据文件(大)】

思路:将维度加载为内存字典,提供给数据文件查询
输入文件1:dict(行政区划代码)

110000,北京市
110101,东城区
110102,西城区
110105,朝阳区
110106,丰台区
110107,石景山区
110108,海淀区
110109,门头沟区
110111,房山区
110112,通州区
...

输入文件2:data(包含行政区划代码的地理数据,已脱敏)

341204,〇营,115.630000,33.110000
420107,〇〇〇〇专卖店,114.380000,30.630000
130705,〇〇〇〇道,115.150000,40.540000
340521,〇〇〇〇(〇〇村店),118.820000,31.380000
330523,〇〇〇〇〇〇(〇〇路店),119.660000,30.790000
370781,〇〇〇衣(〇〇站),118.530000,36.690000
513433,〇〇〇〇饭店,102.210000,28.550000
652926,〇〇〇〇销售中心,81.860000,41.790000
431202,〇〇〇〇特价处理,110.020000,27.570000
110115,停车场(〇〇〇〇〇〇〇〇),116.430000,39.760000
...

命令:

awk -F',' '{if(FILENAME=="dict"){m[$1]=$2}else{print m[$1]","$0}}' dict data

其中

FILENAME:是awk的内置变量,即当前行所在的文件名。注意,通过管道输入的数据不存在文件名

输出:附带行政区划名称的地理数据

颍泉区,341204,〇营,115.630000,33.110000
青山区,420107,〇〇〇〇专卖店,114.380000,30.630000
宣化区,130705,〇〇〇〇道,115.150000,40.540000
当涂县,340521,〇〇〇〇(〇〇村店),118.820000,31.380000
安吉县,330523,〇〇〇〇〇〇(〇〇路店),119.660000,30.790000
青州市,370781,〇〇〇衣(〇〇站),118.530000,36.690000
冕宁县,513433,〇〇〇〇饭店,102.210000,28.550000
拜城县,652926,〇〇〇〇销售中心,81.860000,41.790000
鹤城区,431202,〇〇〇〇特价处理,110.020000,27.570000
大兴区,110115,停车场(〇〇〇〇〇〇〇〇),116.430000,39.760000
...
如果要深入了解

提供几个方向,可自行学习:

awk命令参数awk支持的运算符awk内置函数(比如print/match等)awk内置变量(比如NR/NF/FILENAME等)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/733090.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号