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"

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

变量 数据类型 默认值
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 变量

变量 NFNumber 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 变量

变量 NRNumber 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 22 2013 /usr/share/doc/findutils-4.5.11/ChangeLog
-rw-r--r--. 1 root root 371K 47 2018 /usr/share/doc/glib2-2.56.1/NEWS
-rw-r--r--. 1 root root 498K 520 2022 /usr/share/doc/grub2-common-2.02/grub.html
-rw-r--r--. 1 root root 476K 918 2012 /usr/share/doc/man-db-2.6.3/ChangeLog
-rw-r--r--. 1 root root 353K 33 2012 /usr/share/doc/parted-3.1/ChangeLog
-rw-r--r--. 1 root root 709K 95 2014 /usr/share/doc/rpm-4.11.3/ChangeLog.bz2
-rw-r--r--. 1 root root 575K 110 2017 /usr/share/doc/rsyslog-8.24.0/ChangeLog
-rw-r--r--. 1 root root 404K 312 2011 /usr/share/doc/tar-1.26/ChangeLog
-rw-r--r--. 1 root root 536K 929 2015 /usr/share/doc/xz-5.2.2/ChangeLog
-rw-r--r--. 1 root root 510K 629 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编程工具,助力开发者即刻编程。

更多推荐