Linux命令awk的使用技巧

什么是awk

awk是一种处理文本文件的语言,是一个强大的文本分析工具。它可以对文本进行分割、过滤、格式化、计算等操作,还支持用户自定义函数和动态正则表达式等先进功能。awk可以在命令行中使用,也可以作为脚本来使用。

awk的基本语法

awk的基本语法如下:

awk [选项参数] 'script' var=value file(s)
或
awk [选项参数] -f scriptfile var=value file(s)

其中,选项参数可以是以下之一:

  • -F fs--field-separator fs:指定输入文件的字段分隔符,fs是一个字符串或者是一个正则表达式,如-F:
  • -v var=value--assign var=value:赋值一个用户定义变量。
  • -f scripfile--file scriptfile:从脚本文件中读取awk命令。
  • -mf nnn-mr nnn:对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。
  • -W compact--compat-W traditional--traditional:在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。
  • -W copyleft--copyleft-W copyright--copyright:打印简短的版权信息。
  • -W help--help-W usage--usage:打印全部awk选项和每个选项的简短说明。
  • -W lint--lint:打印不能向传统unix平台移植的结构的警告。
  • -W lint-old--lint-old:打印关于不能向传统unix平台移植的结构的警告。
  • -W posix:打开兼容模式。但有以下限制,不识别:\x、函数关键字func、换码序列以及当fs是一个空格时,将新行作为一个字段分隔符;操作符****=不能代替^^=fflush无效。
  • -W re-interval--re-interval:允许间隔正则表达式的使用,参考 (grep中的Posix字符类),如括号表达式 [[:alpha:]]
  • -W source program-text--source program-text:使用program-text作为源代码,可与-f命令混用。
  • -W version--version:打印bug报告信息的版本。

script是包含一系列命令的awk脚本,通常有以下形式:

pattern { action }
pattern { action }
...

其中,pattern是一个匹配条件,可以是正则表达式、关系表达式、逻辑表达式或者特殊模式BEGINEND。action是一个或多个命令,用花括号括起来,命令之间用分号分隔。如果省略pattern,则对所有的输入行执行action;如果省略action,则打印所有匹配pattern的行。

var=value是用户定义的变量,可以在script中使用,也可以在命令行中赋值。

file(s)是输入文件,可以有多个,用空格分隔。如果没有指定输入文件,则从标准输入读取数据。

awk的内置变量

awk有一些内置的变量,用来存储输入、输出和处理过程中的信息。常用的内置变量如下:

  • $n:当前记录的第n个字段,字段间由FS分隔。$0表示完整的输入记录。
  • ARGC:命令行参数的数目。
  • ARGIND:命令行中当前文件的位置 (从0开始算)。
  • ARGV:包含命令行参数的数组。
  • CONVFMT:数字转换格式 (默认值为%.6g)。
  • ENVIRON:环境变量关联数组。
  • ERRNO:最后一个系统错误的描述。
  • FIELDWIDTHS:字段宽度列表,用空格分隔。
  • FILENAME:当前输入文件的名字。
  • FNR:当前文件的记录数。
  • FS:输入字段分隔符 (默认是空格或Tab)。
  • IGNORECASE:如果为真,则进行忽略大小写的匹配。
  • NF:当前记录中的字段数。
  • NR:从开始至今读的记录数。
  • OFMT:数字的输出格式 (默认值是%.6g)。
  • OFS:输出字段分隔符 (默认值是一个空格)。
  • ORS:输出记录分隔符 (默认值是一个换行符)。
  • RS:输入记录分隔符 (默认是一个换行符)。
  • RSTART:最近匹配的字符串的第一个位置。
  • RLENGTH:最近匹配的字符串的长度。
  • SUBSEP:数组下标分隔符 (默认值是\034)。

awk的基本用法

1. 打印整个或部分记录

如果没有提供pattern和action,awk会打印整个输入记录,相当于指定了{ print $0 }作为action。例如:

$ cat test.txt
Tom 20 male
Mary 18 female
Jack 22 male
$ awk '{ print $0 }' test.txt
Tom 20 male
Mary 18 female
Jack 22 male

如果只想打印部分字段,可以指定字段编号,如$1表示第一个字段,$NF表示最后一个字段。例如:

$ awk '{ print $1 }' test.txt
Tom
Mary
Jack
$ awk '{ print $1, $NF }' test.txt
Tom male
Mary female
Jack male

如果想改变输出字段的顺序或格式,可以使用printf函数,它可以指定格式化字符串和对应的变量。例如:

$ awk '{ printf "%s is a %d years old %s\n", $1, $2, $3 }' test.txt
Tom is a 20 years old male
Mary is a 18 years old female
Jack is a 22 years old male

如果想改变输出字段的分隔符,可以设置OFS变量,它的默认值是一个空格。例如:

$ awk 'BEGIN { OFS = "," } { print $1, $2, $3 }' test.txt
Tom,20,male
Mary,18,female
Jack,22,male

如果想在输出的每行前后加上一些内容,可以在printprintf前后加上字符串,用逗号分隔。例如:

$ awk '{ print "Name:", $1, "Gender:", $3 }' test.txt
Name: Tom Gender: male
Name: Mary Gender: female
Name: Jack Gender: male

2.假设有一个文件mail-list.txt,内容如下:

Amelia 555-5553 amelia.zodiacusque@gmail.com F
Anthony 555-3412 anthony.asserturo@hotmail.com A
Becky 555-7685 becky.algebrarum@gmail.com A
Bill 555-1675 bill.drowning@hotmail.com A
Broderick 555-0542 broderick.aliquotiens@yahoo.com R
Camilla 555-2912 camilla.infusarum@skynet.be R
Fabius 555-1234 fabius.undevicesimus@ucb.edu F
Julie 555-6699 julie.perscrutabor@skeeve.com F
Martin 555-6480 martin.codicibus@hotmail.com A
Samuel 555-3430 samuel.lanceolis@shu.edu A
Jean-Paul 555-2127 jeanpaul.campanorum@nyu.edu R
  • 打印文件中的所有行:

awk '{print}' mail-list.txt

  • 打印文件中第一列的内容:

awk '{print $1}' mail-list.txt

  • 打印文件中第二列和第三列的内容,用逗号分隔:

awk '{print $2, $3}' OFS=',' mail-list.txt

  • 打印文件中最后一列为F的行:

awk '$NF=="F" {print}' mail-list.txt

  • 打印文件中包含gmail的行:

awk '/gmail/ {print}' mail-list.txt

  • 打印文件中每行的列数:

awk '{print NF}' mail-list.txt

  • 打印文件中的总行数:

awk 'END {print NR}' mail-list.txt

  • 打印文件中第一列的最长字符串:

awk 'length($1) > max {max = length($1); name = $1} END {print name}' mail-list.txt

  • 打印文件中第二列的电话号码的平均值:

awk '{sum += $2} END {print sum/NR}' mail-list.txt

  • 使用冒号:作为分隔符,打印文件中第三列的域名:

awk -F '@' '{print $2}' mail-list.txt