一部のデータを抽出して並べ替えるファイルがあります。前のファイルには元のデータが含まれており、このファイルは入力です。
参照: 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++) { ... }
使用された最後の配列要素をポイントして、i
indexを使用して作成されたすべての配列要素を繰り返しますj
。print ref[j];
配列要素の内容、つまり保存されたreference:
行を印刷します。print;
現在の行を印刷します。つまりsid:
、i=0;
reference:
配列インデックスを次の行セットの先頭にリセットします。
スクリプトは次の前提に基づいています。
- 入力は一連のブロックで構成され、各ブロックには次のものが含まれます。
- 1つ以上の行で構成される
reference:
シーケンス sid:
一つの線
- 1つ以上の行で構成される
- 最後の行は
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:
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:
行(ある場合)を印刷します。 (入力ファイルが空の場合、ここに空白行は印刷されません。)
スクリプトは次の前提に基づいています。
- すべての
reference:
後ろには1つが来るsid:
- 同じ行のすべてのペア
reference:
と合計は互いに続きます。sid:
sid: