みんなこんにちは、
ファイルの一致する行グループを分割するために空行を使用したいと思います。 awkに初めて触れたので、いくつかの修正を経て、次のように思い出しました。
awk '!($0 in a) {print "\n"; a[$0]}; {print}'
私の心の中で次のように読む
現在行が配列「a」にない場合は、改行文字を印刷し、その行を「a」に追加します。現在の行を印刷します。
テストファイルに対して実行すると、出力は次のようになります。
abc
abc
def
def
def
ghi
つまり、1つではなく2つの空白行が印刷されます。追加のラインはどこから出ますか?
これは私が使用するテストファイルです。
abc
abc
def
def
def
ghi
答え1
接続された配列は必要ありません。
awk 'prev!=""{ print prev!=$0? prev ORS : $0 } { prev=$0 }
END{ if(prev!="") print prev }' infile
出力:
abc
abc
def
def
def
ghi
awk
改行文字を2回印刷する理由は、print
基本的に印刷中の内容+ORS(酸素出力右エココードSprintf "\n"
デフォルトでは、改行文字である区切り文字を代わりに使用する必要があり、print ""
独自のソリューションを使用して次のことができます(いくつかの修正を適用)。
awk '!($0 in a) { if(c++) print "" } { a[$0]; print}' infile
またはより簡潔に:
awk '!($0 in a) && c++{ print ""} ++a[$0]' infile
答え2
$ awk '{print ($0!=p ? s : "") $0; p=$0; s=ORS}' file
abc
abc
def
def
def
ghi
1行ではなく2行の空白行が印刷されるコードのバグは、印刷された値に必要なすべての代わりに使用されていprint "\n"
ました。print ""
ORS
$ awk 'BEGIN{print "---"; print "\n"; print "---"}'
---
---
$ awk 'BEGIN{print "---"; print ""; print "---"}'
---
---
を使用することもできますが、これは単に使用するのではなく、printf "\n"
予想/仮定するハードコーディングされた値を印刷します。ORS
ORS
print ""
私の解決策とあなたの解決策の主な機能的な違いは、あなたのスクリプトは出力の先頭に空白行を印刷しますが、私の解決策はそうではなく(最初の行を印刷した後の設定のおかげで)、完全なs=ORS
入力ファイルを保存することです。a[]
1つの入力ラインのみを保存すると、前の入力ラインが読み取られますp
。
- スクリプトは大量のメモリを使用しているため、大容量の入力ファイルでは失敗する可能性がありますが、私のスクリプトはあらゆるサイズの入力ファイルで動作します。
- 以前に入力のどこにも入力行が表示されなかった場合、スクリプトは空の行を印刷しますが、私のスクリプトは入力が変更されるたびに空の行を印刷するため、入力行が常にグループ化されていない場合は動作が異なります。その他の例:
$ printf 'foo\nbar\nfoo\n'
foo
bar
foo
$ printf 'foo\nbar\nfoo\n' | awk '!($0 in a) {print ""; a[$0]}; {print}'
foo
bar
foo
$ printf 'foo\nbar\nfoo\n' | awk '{print ($0!=p ? s : "") $0; p=$0; s=ORS}'
foo
bar
foo
コードで実行したい操作を実行するために名前付き配列を使用するのではなく、配列を個別に更新するのではなく、慣用的にa[]
配列名を指定してseen[]
テスト中に更新するので、コードは次awk '!seen[$0]++{print ""} 1'
のように慣用的に作成されますawk '!($0 in a) {print ""; a[$0]}; {print}'
。
$ printf 'foo\nbar\nfoo\n' | awk '!seen[$0]++{print ""} 1'
foo
bar
foo
空白行なしで関数を出力するには、次を選択します。
$ printf 'foo\nbar\nfoo\n' | awk '{print (seen[$0]++ ? "" : s) $0; s=ORS}'
foo
bar
foo
$ printf 'foo\nbar\nfoo\n' | awk '!seen[$0]++ && NR>1{print ""} 1'
foo
bar
foo
$ printf 'foo\nbar\nfoo\n' | awk '!seen[$0]++{if (NR>1) print ""} 1'
foo
bar
foo
$ printf 'foo\nbar\nfoo\n' | awk '!seen[$0]++{printf s; s=ORS} 1'
foo
bar
foo
ORSにprintf形式の文字が含まれていると、最後のエントリは失敗します。たとえば、次のようになります。
$ printf 'foo\nbar\nfoo\n' | awk -v ORS='\n%s\n' '!seen[$0]++{printf s; s=ORS} 1'
foo
%s
awk: cmd. line:1: (FILENAME=- FNR=2) fatal: not enough arguments to satisfy format string
`
%s
'
^ ran out for this one
したがって、これが問題の場合は、次のように強く書くことができます。
$ printf 'foo\nbar\nfoo\n' | awk -v ORS='\n%s\n' '!seen[$0]++{printf "%s", s; s=ORS} 1'
foo
%s
%s
bar
%s
foo
%s