Linux 文本处理三剑客之正则表达式与 grep/sed 实战手册2
awk 命令
awk 命令格式
awk [options] 'script' file(s)
awk [options] -f scriptfile file(s)
script,定义如何处理数据。file,是 awk 处理的数据来源,awk 也可以来自其它命令的输出。-f scriptfile从脚本文件中读取awk命令,每行都是一个独立的script。
script 格式如下:
BEGIN { action }
pattern { action }
END { action }
脚本通常是被单引号或双引号包住,一个awk脚本通常由四部分组成:
BEGIN { action }语句块,awk 执行pattern { action }前要执行的脚本。pattern { action }语句块,决定动作语句何时触发,可以是以下任意一种: - 正则表达式:使用通配符的扩展集。 - 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。 - 模式匹配表达式:使用运算符~(匹配)和~!(不匹配)。END { action }语句块,awk 执行pattern { action }后要执行的脚本。- action部分,决定对数据如何处理,由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大刮号内。常见的action包括:变量或数组赋值、输出命令、内置函数、控制流语句。
BEGIN { action }、pattern { action }、END { action }都是可选项目。
awk 工作流
- 第一步: 执行
BEGIN { commands }语句块中的语句。
在awk从输入输出流中读取行之前执行,通常在BEGIN语句块中执行如变量初始化,打印输出表头等操作。
- 第二步:**从文件或标准输入中读取一行,然后执行
pattern { commands }语句块。**它逐行读取数据,从第一行到最后一行重复这个过程,直到读取完所有行。
pattern语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行。
{} 类似一个循环体,会对文件中的每一行进行迭代,通常将变量初始化语句放在BEGIN语句块中,将打印结果等语句放在END语句块中。
- 第三步:当读至输入流末尾时,执行
END { command }语句块。
在awk从输入流中读取完所有的行之后执行,比如打印所有行的分析结果,它也是一个可选语句块。
awk 示例
示例文件
[root@server ~ 17:25:17]# cat << 'EOF' > employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
EOF
示例1: 打印雇员信息。
[root@server ~ 17:26:11]# awk '{ print }' employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
示例2: 通过读取awk脚本,打印雇员信息。
[root@server ~ 17:27:21]# vim commands.awk
{ print }
[root@server ~ 17:27:59]# awk -f commands.awk employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
示例3: 输出特定隔行。
[root@server ~ 17:28:35]# awk '/张三/ { print }' employee.txt
1) 张三 技术部 23
# 等同于
[root@server ~ 17:29:05]# awk '/张三/' employee.txt
1) 张三 技术部 23
示例4: 统计满足特定条件的记录数。
AWK 中的所有变量都不需要初始化,并且会自动初始化为
0。
[root@server ~ 18:27:29]# awk '
> /术/ { count=count+1 }
> END { print "Count="count }' employee.txt
Count=2
示例5: 输出总长度大于 10 的行。
AWK 提供了一个内建的函数 **length(arg)∗∗用于返回字符串‘arg)** 用于返回字符串 `arg)∗∗用于返回字符串‘arg` 的总长度。
如果要获取某行的总长度,可以使用下面的语法:
length($0)。同样的,如果要获取某列/字段的总长度,可以使用语法:
length($n)。如果要判断某行的字符是否大于/小于/等于 N ,可以使用下面的语法:
length($0) > N。
[root@server ~ 18:27:39]# awk 'length($0)>10 { print $0 }' employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
awk 综合示例
雇员信息表如下:
[root@server ~ 18:28:08]# cat employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
示例文件结构:
- 每个雇员独占一行
- 每个雇员都包括以下字段:序号,名字,部门,年龄
- 每一行的多个字段之间使用空白作为分隔符,空白分隔符一般是空格 ( ) 或者制表符
\t
问题
我们想要获得以下输入结果
序号 姓名 部门 年龄
-------------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
-------------------
分析
表头
为了输入表头,我们可以使用下面的 Shell 语句
[root@server ~ 18:28:32]# awk '
> BEGIN { printf "序号\t名字\t部门\t年龄\n-------------------\n" }'
因为表头部分不需要处理输入文件,也不需要处理输入文件中的每一行,所以 AWK 主体代码都是可以忽略的,甚至包括输入文件都是可以忽略的。
运行以上代码,输出结果如下:
序号 名字 部门 年龄
-------------------
数据
对比想要的结果和输入的文件,可以看出输出结果并并没有对每一行做特殊处理,直接显示就好
因此,只需要在 BEGIN 块后面直接添加 { print } ,添加完代码如下:
[root@server ~ 18:29:00]# cat employee.txt | awk '
> BEGIN { printf "序号\t名字\t部门\t年龄\n-------------------\n" }
{ print }
END {printf "-------------------\n"}'
序号 名字 部门 年龄
-------------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
-------------------
如果把 命令写入到脚本文件中,也可以这样执行:
[root@server ~ 18:30:18]# vim commands.awk
BEGIN { printf "序号\t名字\t部门\t年龄\n-------------------\n" }
{ print }
END {printf "-------------------\n"}
[root@server ~ 18:30:37]# awk -f commands.awk employee.txt
序号 名字 部门 年龄
-------------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
-------------------
表尾
最后,从想要的结果中可以看出,整个结果最后还有一行虚线,这个,我们可以通过 END 语句来实现。
END 语句和 BEGIN 语句类似,我们就直接给结果了。
[root@server ~ 18:31:22]# cat employee.txt | awk '
BEGIN { printf "序号\t名字\t部门\t年龄\n-------------------\n" }
{ print }
END { printf "-------------------\n" }'
序号 名字 部门 年龄
-------------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
-------------------
如果把 命令写入到脚本文件中,也可以这样执行:
[root@server ~ 18:31:33]# vim commands.awk
BEGIN { printf "序号\t名字\t部门\t年龄\n-------------------\n" }
{ print }
END { printf "-------------------\n" }
[root@server ~ 18:32:26]# awk -f commands.awk employee.txt
序号 名字 部门 年龄
-------------------
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
-------------------
awk 命令选项
-F 选项
**作用:**用于定义awk程序的分隔符。awk程序的分隔符,是用于分割同一行数据的分割符号。默认分隔符是:空格。
语法:-F fs 或者 --field-separator=fs
**示例:**打印用户名和家目录
[root@server ~ 18:32:34]# head -n1 /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@server ~ 18:33:16]# head -n1 /etc/passwd | awk -F : '{ print $1" "$6 }'
root /root
-v 选项
**作用:**用于预定义一些变量。这些预定义变量会在执行AWK 程序之前就定义好,准确的说,在 BEGIN 语句之前就已经定义。
示例:
- 借助
-v选项,可以将来自外部值(非stdin)传递给awk。
[root@server ~ 18:34:38]# VAR=10000
[root@server ~ 18:35:23]# echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
10000
- 定义内部变量接收外部变量。
[root@server ~ 18:35:34]# var1="aaa"
[root@server ~ 18:36:03]# var2="bbb"
[root@server ~ 18:36:11]# echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
aaa bbb
[root@server ~ 18:36:23]# awk '{ print v1,v2 }' v1=$var1 v2=$var2 /etc/hostname
aaa bbb
-p 选项
**作用:**用于将当前的 awk 程序代码格式化并且输出到 file 文件中。
格式:--profile[=file] 或者 -p[file]
awk默认导出的 awk 程序代码格式化代码到 awkvars 文件中。
示例:
[root@server ~ 18:36:48]# awk --profile '
BEGIN { printf"---|Header|--\n" }
{ print }
END { printf"---|Footer|---\n" }' employee.txt > /dev/null
运行上面的 awk 程序,awkprof.out 内容如下:
[root@server ~ 18:37:13]# cat awkprof.out
# gawk profile, created Tue Jun 2 18:37:13 2026
# BEGIN block(s)
BEGIN {
printf "---|Header|--\n"
}
# Rule(s)
{
print $0
}
# END block(s)
END {
printf "---|Footer|---\n"
}
awk 内置变量
在上述的示例中,我们使用了很多 awk 内置变量。
我们可以使用 -d 选项,导出awk内置的预定义变量到 file 文件中。
格式:--dump-variables[=file] 或者-d[file]
awk默认导出的内置的预定义变量保存在文件 awkvars 文件中。
示例:
[root@server ~ 18:38:30]# awk -d ''
[root@server ~ 18:39:14]# cat awkvars.out
ARGC: 1
ARGIND: 0
ARGV: array, 1 elements
BINMODE: 0
CONVFMT: "%.6g"
ERRNO: ""
FIELDWIDTHS: ""
FILENAME: ""
FNR: 0
FPAT: "[^[:space:]]+"
FS: " "
IGNORECASE: 0
LINT: 0
NF: 0
NR: 0
OFMT: "%.6g"
OFS: " "
ORS: "\n"
RLENGTH: 0
RS: "\n"
RSTART: 0
RT: ""
SUBSEP: "\034"
TEXTDOMAIN: "messages"
我们把上面的输出结果整理如下:
| 变量 | 数据类型 | 默认值 |
|---|---|---|
| ARGC | number | 1 |
| ARGIND | number | 0 |
| ARGV | array | 1 elements |
| BINMODE | number | 0 |
| CONVFMT | string | “%.6g” |
| ERRNO | number | 0 |
| FIELDWIDTHS | string | “” |
| FILENAME | string | “” |
| FNR | number | 0 |
| FS | string | " " |
| IGNORECASE | number | 0 |
| LINT | number | 0 |
| NF | number | 0 |
| NR | number | 0 |
| OFMT | string | “%.6g” |
| OFS | string | " " |
| ORS | string | “\n” |
| RLENGTH | number | 0 |
| RS | string | “\n” |
| RSTART | number | 0 |
| RT | string | “” |
| SUBSEP | string | “\034” |
| TEXTDOMAIN | string | “messages” |
上面列出的这些变量,在 AWK 中扮演整重要的作用。
| 变 量 | 描述 |
|---|---|
| ARGC | 命令行参数的数目。 |
| ARGIND | 命令行中当前文件的位置(从0开始算)。 |
| ARGV | 包含命令行参数的数组。 |
| CONVFMT | 数字转换格式(默认值为%.6g) |
| ENVIRON | 环境变量关联数组。 |
| ERRNO | 最后一个系统错误的描述。 |
| FIELDWIDTHS | 字段宽度列表(用空格键分隔)。 |
| FILENAME | 当前文件名。 |
| FNR | 同NR,但相对于当前文件。 |
| FS | 字段分隔符(默认是任何空格)。 |
| IGNORECASE | 如 果为真,则进行忽略大小写的匹配。 |
| NF | 当前记录中的字段数。 |
| NR | 当前记录数(读入第几行数据)。 |
| OFMT | 数字的输出格式(默认值是%.6g)。 |
| OFS | 输 出字段分隔符(默认值是一个空格)。 |
| ORS | 输出记录分隔符(默认值是一个换行符)。 |
| RLENGTH | 由 match函数所匹配的字符串的长度。 |
| RS | 记录分隔符(默认是一个换行符)。 |
| RSTART | 由 match函数所匹配的字符串的第一个位置。 |
| SUBSEP | 数组下标分隔符(默认值是\034)。 |
| $n | 当前记录的第n个字段,字段间由 FS分隔。 |
| $0 | 完整的输入记录。 |
NF 变量
变量 NF 是 Number of Fields 的缩写,用于表示当前行分割出来的字段/列数量。
下面的命令输出了当前每一行有被分割出了多少个字段。
[root@server ~ 18:39:22]# echo -e "一 二\n一 二 三\n一 二 三 四" | awk '{ print $0 "\t",NF }'
一 二 2
一 二 三 3
一 二 三 四 4
变量 NF 还可以用于条件判断。
例如,下面的命令输出了哪些字段/列大于 2 的行。
[root@server ~ 18:39:51]# echo -e "一 二\n一 二 三\n一 二 三 四" | awk 'NF > 2'
一 二 三
一 二 三 四
NR 变量
变量 NR 是 Number of Record 的缩写,用于表示当前行的行号,也就是处理到第几行。行编号从 1 开始。
下面的代码,用于在每一行数据的前面输出当前的行号
[root@server ~ 18:40:14]# echo -e "一 二\n一 二 三\n一 二 三 四" | awk '{ print NR". "$0 }'
1. 一 二
2. 一 二 三
3. 一 二 三 四
变量 NR 还可以用于条件判断,例如下面的命令用于输出行号小于 3 的行,也就是第一第二行。
[root@server ~ 18:40:34]# echo -e "一 二\n一 二 三\n一 二 三 四" | awk 'NR < 3'
一 二
一 二 三
$N
对于 AWK 来说,默认的分割符是空白符号,包括 “\t” 和空格 " "。当 AWK 使用分割符对每一行进行分割后,会对每一列/字段进行编号:
$1 $2 $3 ...,分别代表第1列,第2列,第3列…$0,代表一整行。
示例1:
[root@server ~ 18:40:54]# head -n1 /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@server ~ 18:41:20]# head -n1 /etc/passwd |awk '{ print $0 }'
root:x:0:0:root:/root:/bin/bash
[root@server ~ 18:41:34]# head -n1 /etc/passwd |awk -F : '{ print $1" "$6 }'
root /root
示例2: 统计 /usr/share/doc 目录下文件size最大的前10个,并显示文件size。
[root@server ~ 18:41:52]# find /usr/share/doc -type f -ls | awk '{print $7,$11}' | sort -nr | head
725773 /usr/share/doc/rpm-4.11.3/ChangeLog.bz2
588023 /usr/share/doc/rsyslog-8.24.0/ChangeLog
548330 /usr/share/doc/xz-5.2.2/ChangeLog
521349 /usr/share/doc/yum-3.4.3/ChangeLog
509135 /usr/share/doc/grub2-common-2.02/grub.html
486761 /usr/share/doc/man-db-2.6.3/ChangeLog
412827 /usr/share/doc/tar-1.26/ChangeLog
411052 /usr/share/doc/findutils-4.5.11/ChangeLog
379303 /usr/share/doc/glib2-2.56.1/NEWS
360877 /usr/share/doc/parted-3.1/ChangeLog
# 如何让文件的单位使用human size显示?
[root@server ~ 18:42:11]# find /usr/share/doc -type f -ls | awk '{print $7,$11}' | sort -nr | head |awk '{print $2}' | xargs ls -lh
-rw-r--r--. 1 root root 402K 2月 2 2013 /usr/share/doc/findutils-4.5.11/ChangeLog
-rw-r--r--. 1 root root 371K 4月 7 2018 /usr/share/doc/glib2-2.56.1/NEWS
-rw-r--r--. 1 root root 498K 5月 20 2022 /usr/share/doc/grub2-common-2.02/grub.html
-rw-r--r--. 1 root root 476K 9月 18 2012 /usr/share/doc/man-db-2.6.3/ChangeLog
-rw-r--r--. 1 root root 353K 3月 3 2012 /usr/share/doc/parted-3.1/ChangeLog
-rw-r--r--. 1 root root 709K 9月 5 2014 /usr/share/doc/rpm-4.11.3/ChangeLog.bz2
-rw-r--r--. 1 root root 575K 1月 10 2017 /usr/share/doc/rsyslog-8.24.0/ChangeLog
-rw-r--r--. 1 root root 404K 3月 12 2011 /usr/share/doc/tar-1.26/ChangeLog
-rw-r--r--. 1 root root 536K 9月 29 2015 /usr/share/doc/xz-5.2.2/ChangeLog
-rw-r--r--. 1 root root 510K 6月 29 2011 /usr/share/doc/yum-3.4.3/ChangeLog
awk 运算符
| 运算符 | 描述 |
|---|---|
| =、+=、-=、*=、/=、%=、^=、**= | **赋值运算符:**直接赋值、加赋值、减赋值、乘赋值、除赋值、求余赋值、求幂赋值 |
| +、-、*、/、%、^、 | **算数运算符:**加、减、乘、除、求余、求幂 |
| ++、– | **自增和自减运算符:**自增1、自减1,作为前缀或后缀 |
| +、- | **一元运算符:**一元加、一元减 |
| <、<=、>、>=、==、!= | **关系运算符:**小于、小于等于、大于、大于等于、等于、不等于 |
| ~、!~ | **正则表达式运算符:**匹配和不匹配 |
| &&、||、! | **逻辑运算符:**逻辑与、逻辑或、逻辑非 |
[root@server ~ 18:42:29]# awk 'BEGIN { x=5;x=x+2;print "x=",x }'
x= 7
[root@server ~ 18:43:04]# awk 'BEGIN { x=5;x=x-2;print "x=",x }'
x= 3
[root@server ~ 18:43:15]# awk 'BEGIN { x=5;x=x*2;print "x=",x }'
x= 10
[root@server ~ 18:43:22]# awk 'BEGIN { x=5;x=x/2;print "x=",x }'
x= 2.5
[root@server ~ 18:43:27]# awk 'BEGIN { x=5;x=x%2;print "x=",x }'
x= 1
[root@server ~ 18:43:34]# awk 'BEGIN { x=5;x=x^2;print "x=",x }'
x= 25
[root@server ~ 18:43:40]# awk 'BEGIN { x=5;x=x**2;print "x=",x }'
x= 25
更多推荐




所有评论(0)