Try to use Multidimensional Arrays in awk 中,使用!a[$0]++刪除重複行。但對其實現原理,不甚明白,故嘗試分析,以期能理解其實現原理。

Introductions

awk中 * $0代表整行數據,$1代表每行數據使用分隔符分割後的第一個字段,$2代表代表第二個字段,依次類推,而NF即代表每行的字段數; * array[index­expression]代表數組,定義數組不用預先定義數組長度,index­expression是索引,可以是數值或字符串; * !是邏輯操作符,取反,是的意思; * ++是賦值操作符,先運算,後賦值(自增1); * bool判斷爲false的有:數字0、空字符串""、未定義變量(根據使用情況,對應轉換爲數字0、空字符串"") * 默認的action(動作)是{print $0},打印整行,不指定即代表打印整行

仍以Try to use Multidimensional Arrays in awk中的問題爲例

[flying@lemp ~]$ ps -ef | awk -v FS=' ' '$1~/^[^UID]/{user[$1,"count"]+=1;user[$1,"sum"]+=$2}END{for(i in user){split(i,j,SUBSEP);print j[1],user[j[1],"count"],user[j[1],"sum"]}}' | sort
apache 5 9145
apache 5 9145
avahi 2 1470
avahi 2 1470
chrony 1 734
chrony 1 734
colord 1 1863
colord 1 1863
dbus 1 703
dbus 1 703
flying 80 250833
flying 80 250833
libstor+ 1 701
libstor+ 1 701
mysql 1 1518
mysql 1 1518
nobody 1 1790
nobody 1 1790
polkitd 1 854
polkitd 1 854
postfix 2 3311
postfix 2 3311
root 156 107248
root 156 107248
rtkit 1 711
rtkit 1 711
zabbix 33 184938
zabbix 33 184938
[flying@lemp ~]$

Inverse Derivation

嘗試從結果推導過程

抽出以下數據進行分析

apache 5 9145
apache 5 9145

awk的默認的action(動作)是{print $0}(打印整行),不指定即代表打印整行

暫不清楚打印的是第幾行,但可以確定必須打印一行

Assumption

對第一行而言,a[$0]以整行數據爲數組a的index,因爲未給定具體元素值,屬於未定義變量,其默認值可能是數值0或空字符串"",故a[$0]本身的bool判斷是false

假設先執行++再執行,則先自增變成1,再取反變成0,則第一行的bool判斷是false,第一行不打印

在第一行不打印的前提下,可推得打印的是第二行。

第一行的a[$0]因自增變成1,在第二行順序進行++,第二行的bool判斷是false,即第二行不打印,得到的結果是兩行數據都不輸出,這與必須打印一行的前提矛盾。

故而可以確定是先執行!再執行++(先取反變成1,再自增變成2???),第一行的bool判斷是true,第一行打印。

綜上所述,可得到以下結論 * !++的執行順序是,先執行!再執行++ * 打印第一行

根據只打印第一行,可得到第一行的bool判斷結果爲true,而第二行的bool判斷結果爲false的結果

則可復原操作過程 * 對第一行,a[$0]的bool值是false,取反後變成1,bool值是true,打印,自增後變成2 * 對第二行,a[$0]是2,取反後後變成0,bool值是false,不打印,自增後變成1

這是暫時的推論,尚不知是否正確,需要驗證。

Verification

驗證

[flying@lemp ~]$ awk 'BEGIN{a=0;print !a++,a}'
1 1
[flying@lemp ~]$ awk 'BEGIN{a=1;print !a++,a}'
0 2
[flying@lemp ~]$ awk 'BEGIN{a=2;print !a++,a}'
0 3
[flying@lemp ~]$ awk 'BEGIN{a=3;print !a++,a}'
0 4
[flying@lemp ~]$ awk 'BEGIN{a=4;print !a++,a}'
0 5
[flying@lemp ~]$
  • a=0時,!a++是1,bool值true,a變成1
  • a=1時,!a++是0,bool值false,a變成2
  • a=2時,!a++是0,bool值false,a變成3
  • a=3時,!a++是0,bool值false,a變成4
  • a=4時,!a++是0,bool值false,a變成5

分析 * a=0時,!a++變成1,可理解爲是因爲!取反後得到1,而a未變成2而是1,則說明!aa++是分開的,!a的值判斷整行是否未true,a++的值是a當前的值,互不影響。(!++的先後執行順序,仍可通過假設推導得出是先執行!,後執行++) * 根據第一條分析結果來分析a=1!a取反變成0,整行bool值爲false,a++a的值變成2 * a=2!a取反變成0,整行bool值爲false,a++a的值變成3 * a=3!a取反變成0,整行bool值爲false,a++a的值變成4 * a=4!a取反變成0,整行bool值爲false,a++a的值變成5

Conclusion

根據假設和驗證,可得出以下結論: * !++的執行順序是先執行!a再執行a++,但二者互不影響 * !aa++中用於計算的a是初始a值 * !a決定了整行的bool判斷,a++決定了計算後a的值 * 打印第一行

[flying@lemp ~]$ ps -ef | awk -v FS=' ' '$1~/^[^UID]/{user[$1,"count"]+=1;user[$1,"sum"]+=$2}BEGIN{print " username | count | sum\n---------------------------"}END{for(i in user){split(i,j,SUBSEP);printf "%-10s %-8d %-8d\n",j[1],user[j[1],"count"],user[j[1],"sum"]}}END{print "============================"}' | awk '!a[$0]++'
 username | count | sum
---------------------------
flying     79       260273  
rtkit      1        711     
colord     1        1863    
postfix    2        9866    
root       154      134370  
apache     5        31260   
chrony     1        734     
libstor+   1        701     
avahi      2        1470    
zabbix     33       184938  
polkitd    1        854     
dbus       1        703     
nobody     1        1790    
mysql      1        1518    
============================
[flying@lemp ~]$

References

Change Logs

  • 2016.03.07 11:30 Mon Asia/Beijing
    • 初稿完成

  • Note Time: 2016.03.07 11:30 Mon
  • Note Location: Asia/Beijing
  • Writer: lempstacker