sed(またはawk)を使用して、特定のフィールドに同じ値を含む連続行を要約しますか?

sed(またはawk)を使用して、特定のフィールドに同じ値を含む連続行を要約しますか?

これまで歩くことができました。sed複数行にまたがる前後に表示するような高度な機能がありますが、次のことを達成する方法を知りたいです。sedたとえば、私のやり方が次のように感じるからです。Python不要で、フィルタダクト内でも実行可能ガイドライン

着信データを削除する例は次のとおりです。

1b41cf70 0
1cb8dd19 1
620f0b67 2
620f0b67 3
f35d35fe 4
3a6fb62a 5
620f0b67 6
620f0b67 7
620f0b67 8
b958a7ea 9
f35d35fe 10
f35d35fe 11
620f0b67 12

最初の列は常に同じ幅(短縮ハッシュを含む)であり、2番目の列の内容は完全に整列され、数字であり、間隔がありません(したがって、より長いリストに方向を提供する場合を除いては必要ありません)。

必要な出力は次のとおりです(最後の連続発生のインデックスを追加の列に入れます)。

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

あるいは、集計された重複値の数(数学的表現(加算))を使用する方が良い方法です。アッしかし、私のスキルが悪いので、これは他の理想的な結果を説明するためのものです。)

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

私はSO空間で見つけた類似していますが、他のいくつかの質問を追跡しようとしましたが、sed '$!N;/^\([^\ ]\+\)\ [0-9]\+\n\1\ /{P;d}' sampledataインデックスが3,7,11の理由など、解決策につながる可能性があるより簡単な部分が何であるかを頭を閉じることはできません。行を切り捨てるためにNot 8の代わりに使用されます。

私のシステムにはGNU sedバージョン4.8とawkバージョン5.1.0がインストールされており、そのうちの1つを使ってこれを行う方法を知りたいです。いいえ、これは宿題ではなく、圧縮して比較する必要がある冗長性の長いハッシュリストです。 ;)

答え1

元の2番目の列を完全に無視すると、それを使用して連続行uniq -cで文字列が繰り返される回数を計算できます。

2つのフィールドの出力を使用すると、文字列が複数回繰り返されるたびに3番目のフィールドを作成できますuniq -c(テーブルからフィールドが発生した回数+xから1を引いた値)。x次に、最初の2つのフィールドを並べ替えて印刷します。

cut -d ' ' -f 1 file |
uniq -c |
awk '$1 > 1 { $3 = "+" $1 - 1 } { nr += $1; $1 = $2; $2 = nr - 1 - $3; print }'

このnr変数は、ソースファイルの行番号を表します。

質問のデータ出力を提供します。

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

答え2

使用awk:

awk 'function prnt() { print buf, preV; preK=$1; preV=""; buf=$0 }
preK!=$1             { prnt(); next } { preV=$2 }
END                  { prnt() }' infile

出力:

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

awk 'function prnt() { print buf, (c?"+"c:""); preK=$1; c=0; buf=$0 }
preK!=$1             { prnt(); next } { c++ }
END                  { prnt() }' infile

出力:

1b41cf70 0
1cb8dd19 1
620f0b67 2 +1
f35d35fe 4
3a6fb62a 5
620f0b67 6 +2
b958a7ea 9
f35d35fe 10 +1
620f0b67 12

答え3

あなたはそれを要求しましたsed。以下は、自分の試みに近い2つのバージョンですが、POSIXを使用することです。むしろ 拡張正規表現。どちらもパターンスペースに最大2行を保持します。

sed -E '
    :Q
    $!N
    /^([^ ]+) ([0-9]+)( [0-9]+)?\n\1 ([0-9]+)$/{
        s//\1 \2 \4/
        bQ
    }
    P
    D
' -- file

どこ:

  • $!最後の行に改行文字を追加し()、現在の行に次の行を追加しない限り(N
  • 一致式は/…/フィールド1と2を\1キャプチャし、最後に\2可能な最後のインデックスは次の行\3のインデックスは次のようになります。\4
  • フィールド1が次の行で繰り返されると、パターンスペース全体がフィールド1(ハッシュ)、フィールド2(最初のインデックス)、および最後のインデックスに置き換えられ、スクリプトの先頭に分岐します。コマンドの空の正規表現は次のsとおりです。再適用された最後の正規表現を使用する(in /…/
  • それ以外の場合は、最初の行(P;D;)を印刷して削除し、ループを再開します。

出力:

1b41cf70 0
1cb8dd19 1
620f0b67 2 3
f35d35fe 4
3a6fb62a 5
620f0b67 6 8
b958a7ea 9
f35d35fe 10 11
620f0b67 12

代わりに:

/^([^ ]+) ([0-9]+)( ([+]+))?\n\1 [0-9]+$/{
    s//\1 \2 \4+/

出力は次のとおりです。

1b41cf70 0
1cb8dd19 1
620f0b67 2 +
f35d35fe 4
3a6fb62a 5
620f0b67 6 ++
b958a7ea 9
f35d35fe 10 +
620f0b67 12

sed計算が好きではありませんが、できます。


sed最後に、POSIXを使用するスクリプトのいくつかの説明があります。着替えるS

  • []エスケープ文字を除き、s内の文字をエスケープしないでください。 、]および可能なエスケープ文字-
  • BREの記号は+数量子ではなく、一般的なプラス記号です。
  • 空白文字をエスケープする必要はありません。
  • }移植性のために、編集コマンドのリストを終了する前にセミコロンを使用してください。
  • このdコマンドは、最初の改行文字だけでなく、パターン領域全体を削除します。

答え4

高速で混乱する複数行の検索と置換ソリューション(今回はPerlで)

perl -0pe 's/(\w+) (\d+)(\n\1 (\d+))+/$1 $2 $4/g' file

対応する(gnu)sedバージョンは次のとおりです。

sed -rz 's/(\w+) ([0-9]+)(\n\1 ([0-9]+))+/\1 \2 \4/g' file

「+」出力の場合は、いくつかの追加計算を実行する必要があります。

perl -0pe 's/(\w+) (\d+)(\v\1 (\d+))+/"$1 $2 +" . ($4-$2)/ge' file

関連情報