ファイルから抽出および再配置

ファイルから抽出および再配置

一部のデータを抽出して並べ替えるファイルがあります。前のファイルには元のデータが含まれており、このファイルは入力です。

参照: cve, 2017-8962
西ドイツ:45885
参照: cve, 2016-10033
参照: cve, 2016-10034
参照: cve, 2016-10045
参照: cve, 2016-10074
西ドイツ:45917
リファレンス: cve, 2017-8046
西ドイツ:45976
リファレンス: cve, 2018-6577
リファレンス: cve, 2018-6578
西ドイツ:46062

次のファイルは、必須出力を含む新しいファイルです。

参照: cve, 2017-8962
西ドイツ:45885
参照: cve, 2016-10033
西ドイツ:45917
参照: cve, 2016-10034
西ドイツ:45917
参照: cve, 2016-10045
西ドイツ:45917
参照: cve, 2016-10074
西ドイツ:45917
リファレンス: cve, 2017-8046
西ドイツ:45976
リファレンス: cve, 2018-6577
西ドイツ:46062
リファレンス: cve, 2018-6578
西ドイツ:46062

注:たとえば、sid:45917には4つの参照があります(参照:cve、2016-10033参照:cve、2016-10034参照:cve、2016-10045参照:cve、2016-10074)。 sidは他のsidに追加されます(注:sidの後には常に参照が続きます。)、このように重複したブロックがあるため、複数の参照がある場合は、新しいファイルの順序で追加する必要があります。

答え1

使用しているようです。後で sid:s(複数references:の後にシングルsids:=>ペアreferences:合計sid:)、2つのソリューション。


解決策1:反転

単にtacコマンドを使用してください(それは入力と出力の逆順(逆順):tac input | awk | tac > output

awk部分の場合、sをコピーするだけですsid:

gawk '/^sid:/{sid=$0};/^reference:/{print sid "\n" $0}'

解決策2:配列

到着するとすぐに配列に保存し、そのreference:項目に出会うと吐き出します。sid:

gawk 'BEGIN{r=0};/^reference:/{ref[r++]=$0};/^sid:/{for(n=0;n<r;n++){print ref[n] "\n" $0};r=0}' /tmp/test.txt

/^reference:/{ref[r++]=$0}:ref ...で始まる各行に対して、行を配列に格納し、 "r"ポインタを次の要素に移動します。

/^sid:/{for(n=0;n<r;n++){print ref[n] "\n" $0};r=0}:行がsidで始まるたびにrポインタ(for ...)まで配列全体を繰り返し、要素ごとに保存されている参照と現在の行(= sid)を印刷し、rを再起動にリセットします。次の参照を参照して再起動できます。

答え2

awk 'BEGIN { i=0; }
/^reference:/ { ref[i++] = $0; }
/^sid:/ { for(j=0; j<i; j++) { print ref[j]; print; } i=0; }' inputfile > outputfile

説明する:

  • BEGIN { i=0; }0空の文字列ではなく数値として解釈されるように変数を初期化します""
  • /^reference:/ { ref[i++] = $0; }reference:^行開始の基準点)で始まる各行について、行全体を配列要素$0にコピーし、ref[i]インデックスを増やします。i++
  • /^sid:/ { ... }sid:次に始まるすべての行について
  • for(j=0; j<i; j++) { ... }使用された最後の配列要素をポイントして、iindexを使用して作成されたすべての配列要素を繰り返しますj
  • print ref[j];配列要素の内容、つまり保存されたreference:行を印刷します。
  • print;現在の行を印刷します。つまりsid:
  • i=0;reference:配列インデックスを次の行セットの先頭にリセットします。

スクリプトは次の前提に基づいています。

  • 入力は一連のブロックで構成され、各ブロックには次のものが含まれます。
    • 1つ以上の行で構成されるreference:シーケンス
    • sid:一つの線
  • 最後の行はsid:1行でなければなりません。
  • 一致しない行は無視されます。

元の質問では、変換の方向が間違っていると仮定しました。 2番目のスクリプトは反対方向に変換します。

awk 'BEGIN { oldsid=""; ref=""; }
/^reference:/ { ref=$0; }
/^sid:/ { if(oldsid != $0) { if(oldsid != "") print oldsid; } if(ref!="")print ref; oldsid=$0; }
END { if (oldsid != "") print oldsid; }' inputfile > outputfile

説明する:

  • BEGIN { oldsid=""; ref=""; }変数の初期化は明確性のためであり、必ずしも必要ではありません。
  • /^reference:/ { ref=$0; }reference:save the line to Variable で$0始まる各行にはまだref印刷しないでください。
  • /^sid:/ { ... }sid:次に始まるすべての行について
  • if(oldsid != $0) { if(oldsid != "") print oldsid; }行が変更された場合、最後に保存された行は新しい行sid:に属するため、まだ印刷されません。空でない場合は、前の行ブロックが完了したので、今すぐ印刷できます。最初の項目を見つけると空になります。reference:refsid:oldsidreference:sid:oldsidsid:
  • if(ref!="")print ref;保存したファイルがあれば、reference:今印刷してください。 (私たちはその行で前のブロックを閉じたか、sid:現在のブロックが前のブロックとreference:同じであることを知っています。)空の文字列をチェックすることは実際には必要ありません。なぜなら、各行が次のようになるとsid:仮定するからです。sid:前に行がありますreference:
  • oldsid=$0;sid:次の行をインポートするときに比較できるように、現在の行を保存します。現在の行はまだ印刷されていません。
  • END { if (oldsid != "") print oldsid; }最後に最後に保存されたsid:行(ある場合)を印刷します。 (入力ファイルが空の場合、ここに空白行は印刷されません。)

スクリプトは次の前提に基づいています。

  • すべてのreference:後ろには1つが来るsid:
  • 同じ行のすべてのペアreference:と合計は互いに続きます。sid:sid:

関連情報