GAWK 命令 - 1

GNU AWK

gawk 是 GNU 版本的文本处理工具。 在大多数 GNU/Linux 发行版中,gawk 是默认的 awk 实现,因此在日常使用中通常没有区别。

readlink -f /usr/bin/awk
/usr/bin/gawk

gawk 命令默认使用 ERE 模式。

基本语法

gawk [OPTIONS] program file
  • OPTIONS:命令选项。
  • program:这尼玛命令里面还有个程序。
  • file:处理的文件,如果省略,读取 STDIN。

省略 file 为交互模式,输入一行执行一次。

运行过程

  • 读入一行数据:
    • 有匹配规则:
      • 匹配成功:执行相关操作。
      • 匹配失败:不执行操作。
    • 无匹配规则:执行相关操作。

基础用法

创建 foo 文件。

echo -e 'aa 11\nbb 22' > foo

对于每行数据,gawk 默认使用空格/制表符分隔字段。

  • $N:表示第 N 个字段。
  • $0:表示整行数据。
gawk '{print $1}' foo
aa
bb

BEGIN/END 结构

  • BEGIN:初始化,在解释前执行。
  • BODY:对每个记录执行一次。
  • END:结束处理。

注意使用引号 'EOF' 创建文件,这样不会处理特殊字符 $

cat <<'EOF' > foo.gawk
BEGIN {
    FS=":"
    print "User\tShell"
    print "-------\t-------"
}
{
    print $1 "\t" $6
}
END {
    print "-------\t-------"
}
EOF
head -n 3 /etc/passwd | gawk -f foo.gawk
User    Shell
------- -------
root    /root
daemon  /usr/sbin
bin     /bin
------- -------

常用选项

指定分隔符

-F 选项可以修改行的分隔符。

gawk -F: '{print $1}' /etc/passwd | head -n 1

指定文件

-f 选项可以指定文件。

echo '{print $1 "-dir:" $6}' > foo.gawk
gawk -F: -f foo.gawk /etc/passwd | head -n 1
root-dir:/root

变量参数赋值

-v 选项可以在 BEGIN 之前给变量赋值。

gawk -v n=2 'BEGIN{print 2*n}'
4

如果不需要在 BEGIN 中使用,可以不用 -v 参数。

echo 'a b c' | gawk '{print $n}' n=2
b

内置变量

变量 $N

$N 还可以赋值,字符串的双引号不能省略。

echo 'hey man' | gawk '{$2="bro"; print $0}'
hey bro

变量 FS

Field Separator,字段分隔符。

gawk 'BEGIN{FS=":"} {print $1}' /etc/passwd | head -n 1

变量 NF

Number of Fields,表示记录中的字段的数量。

gawk -F: '$1=="root"{print $1":"$NF}' /etc/passwd
root:/bin/bash

变量 NR

Number of Records,表示当前处理的记录编号,默认值为 1,处理一行后会加 1。 可用于跳过文本的第一行,第一行的 NR 值为 1。

cat <<EOF > foo
name score
foo  90
bar  80
EOF
gawk '{if (NR>1) {if ($2>85) {print $1,$2}}}' foo
foo 90

变量 RS

Record Separator,输入记录分隔符,默认值为 \n,表示以换行符分隔每条记录。 将 RS 设置为 "" 表示以空行作为记录分隔符,对于下面的文本,会分为上下 2 个记录。

cat <<EOF > foo
apple
sweet
red

banana
sweet
yellow
EOF

设置 FS="\n",则可通过 $N 获取每行记录。RSFS 通常结合使用。

gawk 'BEGIN{RS=""; FS="\n"} {print $1"\t"$3}' foo
apple  red
banana yellow

变量 OFS

Output Field Separator,输出字段分隔符。

echo 'aa,bb' | gawk 'BEGIN{FS=","; OFS="-"} {print $1,$2}'
aa-bb

变量 FIELDWIDTHS

指定字符宽度进行分隔。

echo 'abbc' | gawk 'BEGIN{FIELDWIDTHS="1 2 1"} {print $1,$2,$3}'
a bb c

条件与结构

条件表达式

==<<=>>=

gawk -F: '$7=="/bin/bash"{print $1}' /etc/passwd

输出所有以 bash 启动的用户。

条件语句

if 里面单条语句可不加 {}

echo -e '10\n20' | gawk '{if ($1>15) print $1}'

if 里面多条语句要加 {}

echo -e '10\n20' | gawk '{if ($1>15) {x=2*$i; print x}}'

单行的 else 语句,前面的语句要加 ; 号。

echo -e '10\n20' | gawk '{if ($1>15) print $1; else print "no"}'

多行不需要加分号。

echo -e '10\n20' | gawk '{
if ($i>15) {
    x=2*$i
    print x
} else {
    print "no"
}
}'

FOR 语句

对每一行的字段求和,+=++ 都支持。

echo '1 2 3' | gawk '{
total=0
for (i=1; i<=NF; i++) {
    total += $i
}
print total
}'

WHILE 语句

对每一行的字段求和。

echo '1 2 3' | gawk '{
i=1
total=0
while (i<=NF) {
    total += $i
    i++
}
print total
}'

DO-WHILE 语句

对每一行的字段求和

echo '1 2 3' | gawk '{
i=1
total=0
do {
    total += $i
    i++
} while(i<=NF)
print total
}'

函数相关

内建函数

  • int(x):取 x 的整数部分。
  • exp(x):x 的指数。
  • sqrt(x):x 的平方根。
  • rand():比 0 大且小于 1 的随机数。
  • length(x):x 的字符串长度。
  • tolower(x):x 转小写。
  • toupper(x):x 转大写。

还有很多,如 gensubgsub

自定义函数

自定义函数必须出现在 BEGIN 块之前。

gawk '
function random(ts, num) {
    srand(ts)
    return int(num * rand())
}
BEGIN {
    ts=systime()
    print ts
    print random(ts, 10)
}'

可以使用函数库文件,再引用。

cat <<'EOF' > funclib.gawk
function random(ts, num) {
    srand(ts)
    return int(num * rand())
}
EOF

gawk 程序文件如下。

cat <<'EOF' > test.gawk
BEGIN {
    ts=systime()
    print ts
    print random(ts, 10)
}
EOF

使用 -f 选项引用两个文件。

gawk -f funclib.gawk -f test.gawk

引用函数库就不能使用内联程序模式,都需要引用。

其它例子

自定义变量

支持数学运算和浮点数,这不比 bash 强 🤪。

gawk 'BEGIN{a=2; a=a*2/3; print a}'
1.33333

数组操作

特点:关联数组,类似字典,无序。

gawk 'BEGIN{arr["name"]="foo"; print arr["name"]}'

可以使用数字下标,其实也是字典。

gawk 'BEGIN{arr[3]="foo"; print arr[3]}'

遍历数组,删除元素。

gawk 'BEGIN{
arr["a"]=1
arr[2]=2
arr["c"]="cat"
delete arr[2]
for (k in arr) {
    print "key:",k," val:", arr[k]
}
}
'
key: a  val: 1
key: c  val: cat

格式化打印

处理浮点数。

gawk 'BEGIN{printf "%.2f\n", 2/3}'
0.67

指定宽度。

echo -e 'foo\nfoobar' | gawk '{printf "%8s\n", $1}'

左对齐。

echo -e 'foo\nfoobar' | gawk '{printf "%-8s\n", $1}'