awk'!a [$ 0] ++'如何工作?


39

这种单行代码无需预先排序即可从文本输入中删除重复的行。

例如:

$ cat >f
q
w
e
w
r
$ awk '!a[$0]++' <f
q
w
e
r
$ 

我在互联网上找到的原始代码为:

awk '!_[$0]++'

这让我更加困惑,因为我_在awk中有特殊的含义,就像在Perl中一样,但事实证明,这只是数组的名称。

现在,我了解了单线背后的逻辑: 每条输入行都用作哈希数组中的键,因此,完成后,哈希按到达顺序包含唯一的行。

我想学习的是awk如何准确地解释此表示法。例如,爆炸符号(!)的含义以及此代码段的其他元素。

它是如何工作的?


标题具有误导性,应为$ 0(零),而不是$ o(o)。
Archemar 2014年

2
由于是哈希,它是无序的,因此“按到达顺序”实际上是不正确的。
凯文(Kevin)

Answers:


35

让我们来看看,

 !a[$0]++

第一

 a[$0]

我们看一下a[$0]a以整个输入行($0)为键的数组)的值。

如果不存在(!测试中的否定结果为true)

 !a[$0]

我们打印输入行$0(默认操作)。

此外,我们在中添加一个(++a[$0],因此下次!a[$0]将评估为false。

很好,找到!您应该看看代码高尔夫!


1
所以本质就是这样:单引号中的表达式被用作awk对每个输入行的测试;每次测试成功awk,都会用大括号执行操作,如果省略则为{print}。谢谢!
Alexander Shcheblikin 2014年

3
@Archemar:这个答案是错误的,请参阅我的。
cuonglm 2014年

@AlexanderShcheblikin中awk的默认操作是{print $0}。这意味着任何评估为true的都将默认执行。因此,例如,awk '1' file打印所有的行,awk '$1' file打印其第一字段不为空或0等所有这些线路
fedorqui

6
@Gnouc在这个答案中我没有看到任何严重的错误。如果这是您要指的是,则实际上在计算表达式的值之后应用增量。的确,增量发生在打印之前,但这是一个很小的不精确性,不会影响基本解释。
吉尔斯(Gillles)“所以-别再作恶了”

1
我在quora中找到了一个新手的最佳解释:qr.ae/TUIVxM
GP92,18年

29

处理如下:

  • a[$0]$0在关联数组中查看key的值a。如果不存在,请创建它。

  • a[$0]++:增加的值a[$0],返回旧值作为expression的值。如果a[$0]不存在,则返回0并递增a[$0]1++运算符返回数字值)。

  • !a[$0]++:否定表达式的值。如果a[$0]++return 0,则整个表达式的计算结果为true,使awk执行的操作为default print $0。否则,整个表达式将被评估为false,导致awk什么都不做。

参考文献:

使用gawk,我们可以使用dgawk(或awk --debug更新版本)来调试gawk脚本。首先,创建一个gawk名为的脚本test.awk

BEGIN {                                                                         
    a = 0;                                                                      
    !a++;                                                                       
}

然后运行:

dgawk -f test.awk

要么:

gawk --debug -f test.awk

在调试器控制台中:

$ dgawk -f test.awk
dgawk> trace on
dgawk> watch a
Watchpoint 1: a
dgawk> run
Starting program: 
[     1:0x7fe59154cfe0] Op_rule             : [in_rule = BEGIN] [source_file = test.awk]
[     2:0x7fe59154bf80] Op_push_i           : 0 [PERM|NUMCUR|NUMBER]
[     2:0x7fe59154bf20] Op_store_var        : a [do_reference = FALSE]
[     3:0x7fe59154bf60] Op_push_lhs         : a [do_reference = TRUE]
Stopping in BEGIN ...
Watchpoint 1: a
  Old value: untyped variable
  New value: 0
main() at `test.awk':3
3           !a++;
dgawk> step
[     3:0x7fe59154bfc0] Op_postincrement    : 
[     3:0x7fe59154bf40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;
dgawk>

您可以看到,Op_postincrement之前已执行Op_not

您也可以使用sistepi代替sstep以更清楚地看到:

dgawk> si
[     3:0x7ff061ac1fc0] Op_postincrement    : 
3           !a++;
dgawk> si
[     3:0x7ff061ac1f40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;

3
@Archemar:您的答案表示!之前已应用++
cuonglm 2014年

6
这个答案是错误的。增量在!算出运算符的结果之后发生。您将运算符优先级(!a[$0]++像一样解析!(a[$0]++))与求值顺序(在a[$0]计算出表达式的值之后,对新值的赋值)进行了混淆。
吉尔(Gilles)'所以

5
@Gnouc它在您引用的段落中说的很对,如果按照您描述的方式工作,则此代码将无法达到预期的效果。首先!x计算该值,其中x是的旧值a[$0]。然后a[$0]设置为1+x
吉尔斯(Gillles)“所以-别再邪恶了”

7
我相信您对awk所做的分析是正确的。抱歉,昨天我另有暗示。但是,您对Archemar答案的批评是​​错误的。Archemar不会误解优先级,您会误会优先级与评估顺序的混淆(请参阅我之前的评论)。如果您删除任何提及Archemar答案的内容,那么您的答案应该是正确的。实际上,它专注于证明Archemar是错误的,但事实并非如此。
吉尔(Gilles)'所以

5
好吧,至少现在我知道awk的调试器了……
Archemar 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.