AWK 实用方法

自从被安利了 AWK 后,对基本的统计,计算逻辑不是特别复杂的任务,首先想到的是用 AWK 来处理,跟其它的语言比,它开发速度快,使用灵活。本文不会对 AWK 做系统的介绍,主要是记录自己在日常工作中常用的一些方法,假定读者已经了解 AWK 的基本语法。详细介绍见 GAWK 手册 或者 man awk

简单命令

  • 查看 awk 版本: awk -W version
  • 时间格式化: strftime("%Y-%m-%d",$4)
  • 读取压缩文件: awk ‘{}’ <(zcat file1.gz) <(gzip -cd file2.gz)
  • 随机数生成:
    • srand( ) : 以当前的系统时间作为随机数的种子。
    • rand( ) : 返回介于 0与1之间的(近似)随机数值。
    • 例 0-9 随机数:awk 'BEGIN{srand();print int(rand()*10);}'
  • 删除 Array: delete, delete array[1]

.awk 文件格式

对于处理逻辑比较复杂的语句,建议写到文件中。

1
2
3
4
5
6
7
8
$ cat test.awk
#!/bin/awk awk -f
BEGIN{}
{
#todo
}
END{}

:END 后面必须在同行内跟一个 {,
否则报错,awk: cmd. line:4: END blocks must have an action part.

多文件读入

通常的使用场景是,多个文件进行字段匹配,进行过滤或统计。例如,需要判断 file2 中的行出现在 file1 中,如果出现则打印。

  • 文本文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ cat file1
    1
    2
    $ cat file2
    1 2
    1 3
    2 5
    3 7
  • ARGIND 读取参数下标, 针对 awk 4.0 以上版本。

    1
    2
    3
    4
    $ awk 'ARGIND==1{map[$1] = 0}ARGIND>1 && $1 in map{print $0}' file1 file2
    1 2
    1 3
    2 5
  • 针对 4.0 以下版本,man awk 发现没有 ARGIND,可以用 ARGC, ARGV, FILENAME 组合。

    1
    $ awk 'ARGV[1]==FILENAME{map[$1] = 0}ARGV[2]==FILENAME&&($1 in map){print $0}' file1 file2
  • 比较 NR 与 FNR

    1
    $ awk 'NR==FNR{map[$1] = 0}NR>FNR && ($1 in map){print $0}' file1 file2

单引号,双引号使用

需要根据分别处理,

1
2
3
4
5
6
$ test="abs"
$ awk 'BEGIN{print '$test'}' # 可以调用命令行变量 test
$ awk 'BEGIN{print "$test"}' # 用双引号会把 $test 变成字符输出
$ awk 'BEGIN{if("'$test'"=="abs"){print "True"}}' #会把test的值得到后用字符比较
$ awk 'BEGIN{print '"$test"'}' #可以按照test的实际值比较
$ awk 'BEGIN{print '''$test'''}' #与第一种是实际效果

awk 内排序

  • 通过内置函数(asort,asorti使用) awk 3.1以上版本才支持

    • asort srcarrlen=asort(srcarr,dscarr):

      默认返回值是:原数组长度,传入参数dscarr则将排序后数组赋值给dscarr. 注:asort只对值进行了排序,因此丢掉原先键值

    • asorti slen=asorti(a,tA):

      asorti对键值进行排序(字符串类型),将生成新的数组放入:tA中

  • 管道发送到sort排序

    1
    awk ' {}END{for(i in a){print i,a[i] | "sort -r -n -k1";}}'
  • 自定义函数, 适用于复杂的排序规则。

: sort 有时候排序结果跟预期有出入, 需要设置 LC_ALL=C awk 语句。

使用外部变量

  • awk '{print a, b}' a=111 b=222 yourfile
    : 变量位置要在 file 名之前, 否则就不能调用。
    :BEGIN{}中是不能调用这些的variable,要用之后所讲的第二种方法才可解决。
  • awk –v a=111 –v b=222 '{print a,b}' yourfile
    : 对每一个变量加一个 –v 作传递.
  • awk '{print "'"$LOGNAME"'"}' yourfile
  • awk '{print \"$LOGNAME\"}' yourfile

使用外部函数

  1. 定义函数

    1
    2
    3
    a() {
    echo $1
    }
  2. export

    1
    export -f a
  3. system 命令

    1
    2
    3
    4
    awk 'BEGIN{
    str="a"
    system("a "str);
    }'

: awk 原始版本不支持,gawk 支持。
system("a "str); 命令用引号包含,否则会当做常量传递到子进程。
system("a "str); 可以使用 print “a”, str |”/bin/bash” 替换。

如果 system()括号里面的参数没有加上双引号的话,awk认为它是一个变量,它会从awk的变量里面把它们先置换为常量,然后再回传给shell。

如果system()括号里面的参数有加上双引号的话,那么awk就直接把引号里面的内容回传给shell,作为shell的“命令行”。

awk无法直接将执行中的部分数据输出给Shell 命令. 且 Shell 命令执行的结果也无法直接输入到awk中。

处理 CSV 文件

  • awk 版本 >= 4.0
    使用 FPAT = “([^,]+)|(\”[^\”]+\”)” 或 FPAT=’([^,]+)|(“[^”]+”)’
  • awk 3.0 +
    FS=”^\”|\”,\”|\”$”, 更多匹配说明,参考1

:如果 csv 字段中包含换行符号,上述两种方法则无法处理。因为 CSV 文件列数为固定值(比如 10), 则可以做预处理:

1
awk FPAT = "([^,]+)|(\"[^\"]+\")" -v fields=10 '{f+=NF; str=(str?str OFS:"") $0} f>=fields{print str; str=""; f=0}' file

位操作

and(v1, v2) Return the bitwise AND of the values provided by v1 and v2.

compl(val) Return the bitwise complement of val.

lshift(val, count) Return the value of val, shifted left by count bits.

or(v1, v2) Return the bitwise OR of the values provided by v1 and v2.

rshift(val, count) Return the value of val, shifted right by count bits.

xor(v1, v2) Return the bitwise XOR of the values provided by v1 and v2.

字符串匹配

  • awk '$2~/888/{print "ok"}' data_pvuv

  • match

    1
    2
    3
    4
    $ awk 'BEGIN{start=match("this is a test",/[a-z]+$/); print start}'
    11
    $ awk 'BEGIN{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }'
    11 11 4

第一个例子,打印以连续小写字符结尾的开始位置,这里是11。

第二个例子,打印RSTART和RLENGTH变量,这里是11(start),11(RSTART),4(RLENGTH)。

:LC_ALL=C 使用与否,可能会导致[a-z] 的 match 结果不一致, 参见 awk 文档

使用管道 ‘|’

  • awk output 指令 | “Shell 接受的命令”,
    • 例 : print $1,$2 | "sort -k 1"
  • “Shell 接受的命令” | awk input 指令,
    • 例 : "ls " | getline

: awk input 指令只有 getline 一个,awk output 指令有 print, printf() 二个。