
特定のデータを抽出して並べ替えたいファイルがあります。古いファイルには生データが含まれています。このファイルは入力です
参照: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:
の後に - 1
sid:
行
- 1行以上の行のシーケンス
- 最後の行は
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:
行があればそれを出力します。(入力ファイルが空の場合、ここで空の行は出力されません。)
このスクリプトは次の前提に基づいています:
- every
reference:
の後にsid:
- 同じ行にある
reference:
とのペアはすべて連続しているsid:
sid: