条件に応じて awk に空行を追加する

条件に応じて awk に空行を追加する

みんなこんにちは、

ファイルの一致する行グループを分割するために空行を使用したいと思います。 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"予想/仮定するハードコーディングされた値を印刷します。ORSORSprint ""

私の解決策とあなたの解決策の主な機能的な違いは、あなたのスクリプトは出力の先頭に空白行を印刷しますが、私の解決策はそうではなく(最初の行を印刷した後の設定のおかげで)、完全なs=ORS入力ファイルを保存することです。a[]1つの入力ラインのみを保存すると、前の入力ラインが読み取られますp

  1. スクリプトは大量のメモリを使用しているため、大容量の入力ファイルでは失敗する可能性がありますが、私のスクリプトはあらゆるサイズの入力ファイルで動作します。
  2. 以前に入力のどこにも入力行が表示されなかった場合、スクリプトは空の行を印刷しますが、私のスクリプトは入力が変更されるたびに空の行を印刷するため、入力行が常にグループ化されていない場合は動作が異なります。その他の例:
    $ 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

関連情報