ファイルから抽出して並べ替える

ファイルから抽出して並べ替える

特定のデータを抽出して並べ替えたいファイルがあります。古いファイルには生データが含まれています。このファイルは入力です

参照:cve,2017-8962
sid:45885
参照:cve,2016-10033
参照:cve,2016-10034
参照:cve,2016-10045
参照:cve,2016-10074
sid:45917
参照:cve,2017-8046
sid:45976
参照:cve,2018-6577
参照:cve,2018-6578
sid:46062

以下のファイルは必要な出力を含む新しいファイルです

参照:cve,2017-8962
sid:45885
参照:cve,2016-10033
sid:45917
参照:cve,2016-10034
sid:45917
参照:cve,2016-10045
sid:45917
参照:cve,2016-10074
sid:45917
参照:cve,2017-8046
sid:45976
参照:cve,2018-6577
sid:46062
参照:cve,2018-6578
sid:46062

説明: たとえば、sid:45917 には 4 つの参照があります (reference:cve,2016-10033 reference:cve,2016-10034 reference:cve,2016-10045 reference:cve,2016-10074)。各参照を分割し、sid を 1 つずつ下に追加する必要があります (注: sid の後には必ず参照が続きます) のように繰り返しブロックが存在するため、複数の参照がある場合は、新しいファイルの順序で追加する必要があります。

答え1

あなたが使っているように後付け sid:(複数references:とその単一のsids:ペア =>references:sid:)、2 つのソリューション。


解決策1:逆転

シンプルなtacコマンドを使用します(入力と出力を逆にするには、逆の順序で実行します。tac input | awk | tac > output

awk 部分については、sid:s を複製するだけです:

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

解決策2: 配列

sを配列に格納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++) { ... }i最後に使用された配列要素の次の要素を指すように、インデックスを使用して書き込まれたすべての配列要素をループしますj
  • print ref[j];配列要素の内容、つまり保存されたreference:行を印刷します。
  • print;現在の行、つまりsid:行を印刷します
  • i=0;reference:次の行のグループの配列インデックスを先頭にリセットします。

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

  • 入力は一連のブロックで構成され、各ブロックには以下が含まれます。
    • 1行以上の行のシーケンスreference:の後に
    • 1sid:
  • 最後の行はsid:行である必要があります。
  • 一致しない行は無視されます。

最初の質問では、変換の方向が間違っていると想定しました。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:「行を$0変数に保存」で始まる行についてはref、まだ印刷しないでください。
  • /^sid:/ { ... }sid:...で始まるすべての行について
  • if(oldsid != $0) { if(oldsid != "") print oldsid; }sid:行が変更された場合、reference:に保存された最後の行はref新しい に属するsid:ため、まだ印刷されません。oldsidが空でない場合は、reference:同じ を持つ前の行ブロックsid:が終了したため、今すぐ印刷できます。oldsid最初の が見つかると、 は空になりますsid:
  • if(ref!="")print ref;保存されている がある場合はreference:、それを今すぐ印刷します。(対応する行で前のブロックを閉じたばかりかsid:、現在の が前のブロックとreference:同じであることがわかっています。) すべての行の前に行があるsid:と想定しているため、空の文字列のチェックは実際には必要ありません。sid:reference:
  • oldsid=$0;sid:次の行を取得したときの比較のために現在の行を保存します。現在の行はまだ印刷されていません。
  • END { if (oldsid != "") print oldsid; }最後に、最後に保存されたsid:行があればそれを出力します。(入力ファイルが空の場合、ここで空の行は出力されません。)

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

  • everyreference:の後にsid:
  • 同じ行にあるreference:とのペアはすべて連続しているsid:sid:

関連情報