このワンライナーは、事前にソートすることなく、テキスト入力から重複する行を削除します。
例えば:
$ cat >f
q
w
e
w
r
$ awk '!a[$0]++' <f
q
w
e
r
$
私がインターネットで見つけた元のコードは次のとおりです:
awk '!_[$0]++'
_
は、Perlのようにawkで特別な意味を持ちますが、配列の名前にすぎません。
今、私はワンライナーの背後にあるロジックを理解しています:各入力行はハッシュ配列のキーとして使用されるため、完了すると、ハッシュには到着順に一意の行が含まれます。
私が学びたいのは、この表記がawkによってどのように解釈されるかです。例えば。何の前兆記号(!
)このコードスニペットの意味とその他の要素。
どのように機能しますか?
どれどれ、
!a[$0]++
最初
a[$0]
a[$0]
(配列a
の入力行全体($0
)をキーとして)の値を調べます。
存在しない場合(!
はテストの否定であり、trueに評価されます)
!a[$0]
入力行$0
(デフォルトアクション)を出力します。
また、1つ(++
)をa[$0]
に追加するため、次回!a[$0]
はfalseと評価されます。
いいですね、見つけて!あなたはコードゴルフを見ておくべきです!
ここに処理があります:
a[$0]
:連想配列a
のキー$0
の値を確認します。存在しない場合は作成してください。
a[$0]++
:a[$0]
の値をインクリメントし、古い値を式の値として返します。 a[$0]
が存在しない場合は、0
を返し、a[$0]
を1
にインクリメントします(++
演算子は数値を返します)。
!a[$0]++
:式の値を否定します。 a[$0]++
が0
を返す場合、式全体がtrueと評価され、awk
にデフォルトアクションprint $0
を実行させます。そうでない場合、式全体がfalseと評価され、awk
は何もしません。
参照:
gawk
を使用すると、 dgawk(またはawk --debug
with new version) を使用してgawk
スクリプトをデバッグできます。まず、test.awk
という名前のgawk
スクリプトを作成します。
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
より前に実行されました。
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++;