概述
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] <defunct>
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等)
awk是一个强大的文本处理工具,常用于Unix/Linux系统。它能按行处理文本,通过字段分割、条件判断和自定义输出实现数据筛选与转换。本文介绍了awk的基本用法,包括设置字段分隔符、条件判断和动作执行,并通过多个实战案例展示了awk在统计计算、进程分析、日志处理和多文件操作等场景的应用。
652

被折叠的 条评论
为什么被折叠?



