awk 命令

awk 命令格式

awk [options] 'script' file(s) 
awk [options] -f scriptfile file(s) 
  1. script,定义如何处理数据。
  2. file,是 awk 处理的数据来源,awk 也可以来自其它命令的输出。
  3. -f scriptfile 从脚本文件中读取awk命令,每行都是一个独立的script。

script 格式如下:

BEGIN { action }
pattern { action }
END { action }

脚本通常是被单引号或双引号包住,一个awk脚本通常由四部分组成:

  1. BEGIN { action } 语句块,awk 执行 pattern { action } 前要执行的脚本。
  2. pattern { action } 语句块,决定动作语句何时触发,可以是以下任意一种: - 正则表达式:使用通配符的扩展集。 - 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。 - 模式匹配表达式:使用运算符~(匹配)和 ~!(不匹配)。
  3. END { action } 语句块,awk 执行 pattern { action } 后要执行的脚本。
  4. 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

示例文件结构:

  1. 每个雇员独占一行
  2. 每个雇员都包括以下字段:序号,名字,部门,年龄
  3. 每一行的多个字段之间使用空白作为分隔符,空白分隔符一般是空格 ( ) 或者制表符 \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
-------------------

print 用命令于输出当前行。

如果把 命令写入到脚本文件中,也可以这样执行:

[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 语句之前就已经定义。

示例:

  1. 借助 -v 选项,可以将来自外部值(非stdin)传递给awk。
[root@server ~ 18:34:38]# VAR=10000 
[root@server ~ 18:35:23]# echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
10000
  1. 定义内部变量接收外部变量。
[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"

我们把上面的输出结果整理如下:

变量数据类型默认值
ARGCnumber1
ARGINDnumber0
ARGVarray1 elements
BINMODEnumber0
CONVFMTstring“%.6g”
ERRNOnumber0
FIELDWIDTHSstring“”
FILENAMEstring“”
FNRnumber0
FSstring" "
IGNORECASEnumber0
LINTnumber0
NFnumber0
NRnumber0
OFMTstring“%.6g”
OFSstring" "
ORSstring“\n”
RLENGTHnumber0
RSstring“\n”
RSTARTnumber0
RTstring“”
SUBSEPstring“\034”
TEXTDOMAINstring“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
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐