awk '! a [$ 0] ++' ทำงานอย่างไร


39

หนึ่งซับนี้จะลบบรรทัดที่ซ้ำกันออกจากการป้อนข้อความโดยไม่ต้องเรียงลำดับล่วงหน้า

ตัวอย่างเช่น:

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

รหัสเดิมที่ฉันพบใน internets อ่านได้:

awk '!_[$0]++'

นี่เป็นสิ่งที่ทำให้ฉันงุนงงมากขึ้นเมื่อฉัน_มีความหมายพิเศษใน awk เช่นใน Perl แต่กลายเป็นชื่อของอาร์เรย์

ตอนนี้ฉันเข้าใจตรรกะเบื้องหลังหนึ่งซับ: แต่ละบรรทัดอินพุตถูกใช้เป็นคีย์ในอาร์เรย์แฮชดังนั้นเมื่อเสร็จสมบูรณ์แฮชจะมีบรรทัดที่ไม่ซ้ำกันตามลำดับการมาถึง

สิ่งที่ฉันต้องการเรียนรู้คือสัญกรณ์นี้ตีความโดย awk ได้อย่างไร เช่นความหมายของสัญญาณบาง ( !) และองค์ประกอบอื่น ๆ ของข้อมูลโค้ดนี้

มันทำงานยังไง?


ชื่อเรื่องทำให้เข้าใจผิดควรเป็น $ 0 (ศูนย์) ไม่ใช่ $ o (o)
Archemar

2
เนื่องจากเป็นแฮชจึงไม่มีการเรียงลำดับดังนั้น "ตามลำดับการมาถึง" จึงไม่ถูกต้อง
เควิน

คำตอบ:


35

มาดูกัน,

 !a[$0]++

เป็นครั้งแรก

 a[$0]

เราดูค่าของa[$0](อาร์เรย์ที่aมีทั้งสายอินพุต ( $0) เป็นคีย์)

หากไม่มีอยู่ (การ!ปฏิเสธในการทดสอบจะกลายเป็นจริง)

 !a[$0]

เราพิมพ์บรรทัดอินพุต$0(การดำเนินการเริ่มต้น)

นอกจากนี้เรายังเพิ่มหนึ่งรายการ++ลงa[$0]ในดังนั้นครั้งต่อไป!a[$0]จะประเมินเป็นเท็จ

ดีหา !! คุณควรดูรหัสกอล์ฟ!


1
ดังนั้นสาระสำคัญคือ: การแสดงออกในเครื่องหมายคำพูดเดียวจะถูกใช้โดยawkการทดสอบสำหรับแต่ละบรรทัดอินพุต; เวลาการทดสอบประสบความสำเร็จทุกคนดำเนินการดำเนินการในวงเล็บปีกกาซึ่งเมื่อมีการละเว้นawk {print}ขอบคุณ!
Alexander Shcheblikin

3
@Archemar: คำตอบนี้ผิดดูของฉัน
cuonglm

@AlexanderShcheblikin ในการดำเนินการเริ่มต้นคือawk {print $0}ซึ่งหมายความว่าสิ่งใดก็ตามที่ประเมินว่าเป็นความจริง ตัวอย่างเช่นawk '1' fileพิมพ์บรรทัดทั้งหมดพิมพ์บรรทัดawk '$1' fileทั้งหมดที่มีฟิลด์แรกไม่ว่างเปล่าหรือ 0 เป็นต้น
fedorqui

6
@Gnouc ฉันไม่เห็นข้อผิดพลาดร้ายแรงในคำตอบนี้ หากนั่นคือสิ่งที่คุณกำลังอ้างถึงการเพิ่มจะถูกนำไปใช้จริง ๆ หลังจากคำนวณค่านิพจน์แล้ว มันเป็นความจริงที่การเพิ่มขึ้นเกิดขึ้นก่อนการพิมพ์ แต่นั่นเป็นความไม่ชัดเจนเล็กน้อยซึ่งไม่ส่งผลต่อคำอธิบายพื้นฐาน
Gilles 'หยุดความชั่วร้าย'

1
ฉันพบคำอธิบายที่ดีที่สุดสำหรับมือใหม่ที่จะเข้าใจที่นี่ใน quora: qr.ae/TUIVxM
GP92

29

นี่คือการประมวลผล:

  • a[$0]: ดูที่ค่าของคีย์ในอาเรย์$0 aหากไม่มีอยู่ให้สร้างขึ้น

  • a[$0]++: เพิ่มค่าของa[$0]ให้คืนค่าเดิมเป็นค่าของนิพจน์ หากa[$0]ไม่มีอยู่ให้ส่งคืน0และเพิ่มค่าa[$0]เป็น1( ++ตัวดำเนินการส่งคืนค่าตัวเลข)

  • !a[$0]++: ลบล้างคุณค่าของการแสดงออก หากa[$0]++ผลตอบแทนจาก0การแสดงออกทั้งได้รับการประเมินให้เป็นจริงให้ดำเนินการดำเนินการเริ่มต้นawk print $0มิฉะนั้นนิพจน์ทั้งหมดจะถูกประเมินว่าเป็นเท็จทำให้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_postincrementOp_not

คุณยังสามารถใช้siหรือstepiแทนsหรือstepเพื่อดูอย่างชัดเจนมากขึ้น:

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

6
คำตอบนี้ผิด การเพิ่มขึ้นเกิดขึ้นหลังจาก!คำนวณผลการดำเนินการ คุณกำลังสับสนกับตัวดำเนินการลำดับความสำคัญ ( !a[$0]++ถูกแยกวิเคราะห์เหมือน!(a[$0]++)) พร้อมลำดับการประเมินผล (การกำหนดค่าใหม่ของการa[$0]เกิดขึ้นหลังจากคำนวณค่านิพจน์แล้ว)
Gilles 'หยุดชั่วร้าย'

5
@Gnouc มันบอกว่าถูกต้องในตอนที่คุณพูดและถ้ามันทำงานตามที่คุณอธิบายรหัสนี้จะไม่มีผลตามที่ต้องการ ครั้งแรกค่า!xคำนวณที่เป็นค่าเก่าx a[$0]จากนั้นมีการตั้งค่าa[$0] 1+x
Gilles 'หยุดความชั่วร้าย'

7
ฉันเชื่อว่าการวิเคราะห์สิ่งที่ awk ทำนั้นถูกต้อง ขออภัยถ้าฉันเป็นนัยเมื่อวาน อย่างไรก็ตามคำติชมของคำตอบของ Archemar ของคุณนั้นผิด Archemar ไม่ได้เข้าใจผิดก่อนหน้าคุณทำคุณสับสนมาก่อนกับลำดับของการประเมิน (ดูความคิดเห็นก่อนหน้าของฉัน) หากคุณลบการกล่าวถึงคำตอบของ Archemar ในตัวคุณคำตอบของคุณควรถูกต้อง ตามที่มันเป็นมันมุ่งเน้นไปที่การพิสูจน์ Archemar ผิดและนี่ไม่ใช่กรณี
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

5
ดีอย่างน้อยตอนนี้ฉันรู้เกี่ยวกับการดีบัก awk ของ ...
Archemar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.