为什么 awk split() 使第一个字段成为数组中的最后一个元素?

为什么 awk split() 使第一个字段成为数组中的最后一个元素?

我可能在这里遗漏了一些非常简单的东西,但是当我说

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print $WORD;
        }
    }'

我得到这样的回报:

quick
brown
fox
jumped
over
the
lazy
dog.
The

为什么第一个字最后打印?

$ awk --version
awk version 20070501

答案1

首先, yieldfor (i in array)中的awk是数组的索引,而不是数组元素。所以你得到了像你访问的结果$1$2....$NF

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORD;       
        }
    }'
2
3
4
5
6
7
8
9
1

您可以看到访问变量时获得了数组索引WORD


对于你的问题,POSIX 定义了通过awk数组循环产生数组索引未指定顺序:

for(数组中的变量)

它将迭代,将数组的每个索引分配给变量 未指定顺序

所以由实现来定义如何来遍历数组。

我的系统中的快速测试表明了这一点,gawkmawk以递增顺序循环:

for AWK in gawk mawk /usr/5bin/[on]awk /usr/5bin/posix/awk; do
  printf '==%s==\n' "$AWK"
  echo 'The quick brown fox jumped over the lazy dog.' |
  "$AWK" '{
    split($0, WORDS, " ")
    for (WORD in WORDS) {
      print WORD;
    }
  }' | { sed 1q; tail -n1 }
 done
==awk==
1
9
==mawk==
1
9
==/usr/5bin/nawk==
2
1
==/usr/5bin/oawk==
2
1
==/usr/5bin/posix/awk==
2
1

(使用GNU sed,你需要sed -u 1q

答案2

您不是打印数组的元素,而是按顺序打印字段。在 中awk,变量没有前缀 a $,那就是字段。因此,$a将打印存储在 中的任何数字的字段a。要打印变量,例如foo,您需要print foo, 不$

当您迭代数组时awk,您正在迭代数组的索引:

$ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORD;
        }
    }'
1
2
3
4
5
6
7
8
9

你所追求的是:

$ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
        split($0, WORDS, " ");
        for ( WORD in WORDS ) {
            print WORDS[WORD];
        }
    }'
The
quick
brown
fox
jumped
over
the
lazy
dog.

在 GNU 中awk,这相当于:

 $ echo 'The quick brown fox jumped over the lazy dog.' |     awk '{
            for (i=1; i<=NF;i++){
            print $i
        }
    }'

虽然gawk(GNU awk)split会按照找到的顺序对数组进行排序(如上所示),但其他实现不会这样做,正如 cuonglm 在他的答案中解释的那样。因此,split您可以设置字段分隔符并使用 let来代替使用awk来进行拆分。在您的示例中,不需要,因为分隔符是空格,但以下是其他情况下的操作方法:

 $ echo 'The-quick-brown-fox-jumped-over-the-lazy-dog.' | 
    awk -F"-" '{
                 for(i=1;i<=NF;i++){
                    print $i
                 }
                }'
The
quick
brown
fox
jumped
over
the
lazy
dog.

答案3

$1忽略在您的示例中,您可以(应该)只打印每个字段、$2等的事实,split返回数组中的元素数量,因此要按照它们出现的顺序循环遍历它们,您可以使用如下所示的内容:

echo 'The quick brown fox jumped over the lazy dog.' | \
    awk '{
        n = split($0, WORDS, " ");
        for (i = 1; i <= n; ++i) {
            print WORDS[i];
        }
    }'

正如其他人提到的,使用时遍历数组的顺序for (indx in array)是未指定的(尽管如果您使用 GNU awk,则可以控制它)。

相关内容