我可以使用 -v 将数组传递给 awk 吗?

Can I pass an array to awk using -v?

我希望能够将数组变量传递给 awk。我指的不是 shell 数组,而是原生的 awk 数组。我知道我可以像这样传递标量变量:

awk -vfoo="1" 'NR==foo' file

我可以使用相同的机制来定义 awk 数组吗?类似于:

$  awk -v"foo[0]=1" 'NR==foo' file
awk: fatal: `foo[0]' is not a legal variable name

我已经尝试了上述的一些变体,但是 none 它们可以在我的 Debian 上的 GNU awk 4.1.1 上运行。那么,是否有任何版本的 awkgawkmawk 或其他版本)可以接受来自 -v 开关的数组?

我知道我可以解决这个问题,并且可以很容易地想到这样做的方法,我只是想知道是否有任何 awk 实现本身支持这种功能。

看来按照定义是不可能的。

man awk 我们有:

-v var=val

--assign var=val

Assign the value val to the variable var, before execution of the program begins. Such variable values are available to the BEGIN rule of an AWK program.

然后我们在 Using Variables in a Program 中读到:

The name of a variable must be a sequence of letters, digits, or underscores, and it may not begin with a digit.

Variables in awk can be assigned either numeric or string values.

因此 -v 实现的定义方式使得无法提供数组作为变量,因为字符 =[ 的任何类型的使用都是不允许的作为 -v 变量传递的一部分。两者都是必需的,因为 awk 中的数组只是关联的。

您可以使用 mawk 或 gawk 中的 split() 函数来拆分“-v”值的输入(这里是 gawk 手册页):

split(s, a [, r [, seps] ])

Split the string s into the array a and the separators array seps on the regular expression r, and return the number of fields.*

此处的示例,其中我传递值 "ARRAYVAR",一个逗号分隔的值列表,它是我的数组,带有“-v”到 awk 程序,然后将其拆分为内部变量数组 "arrayval" 使用 split() 函数然后打印数组的第三个值:

echo 0 | gawk -v ARRAYVAR="a,b,c,d,e,f" '{ split(ARRAYVAR,arrayval,","); print(arrayval[3]) }'
c

似乎有效:)

不幸的是,这是不可能的。但是,您可以使用一些巧妙的方法将 bash 数组转换为 awk 数组。

我最近想通过将 bash 数组传递给 awk 以使用它进行过滤来做到这一点,所以这就是我所做的:

$ arr=( hello world this is bash array )
$ echo -e 'this\nmight\nnot\nshow\nup' | awk 'BEGIN {
  for (i = 1; i < ARGC; i++) {
      my_filter[ARGV[i]]=1
      ARGV[i]="" # unset ARGV[i] otherwise awk might try to read it as a file
  }
} !my_filter[[=10=]]' "${arr[@]}"

输出:

might
not
show
up

对于关联数组,您可以将其作为键值对字符串传递,然后在 BEGIN 部​​分重新格式化。

$ echo | awk -v m="a,b;c,d" '
BEGIN {
  split(m,M,";")
  for (i in M) {
    split(M[i],MM,",")
    MA[MM[1]]=MM[2]
  }
}
{
  for (a in MA) {
    printf("MA[%s]=%s\n",a, MA[a])
  }
}'

输出:

MA[a]=b
MA[c]=d

如果您不坚持使用 -v,您可以使用 -i(包含)来读取包含变量设置的 awk 文件。 像这样:

if F=$(mktemp inputXXXXXX); then
    cat >$F << 'END'
BEGIN {
    foo[0]=1
}
END
cat $F
    awk -i $F 'BEGIN { print foo[0] }' </dev/null
    rm $F
fi

示例跟踪(使用 gawk-4.2.1):

bash -x /tmp/test.sh 
++ mktemp inputXXXXXX
+ F=inputrpMsan
+ cat
+ cat inputrpMsan
BEGIN {
    foo[0]=1
}
+ awk -i inputrpMsan 'BEGIN { print foo[0] }'
1
+ rm inputrpMsan