ブロック内の特定の行を持つ行ブロックをソートする方法は?

ブロック内の特定の行を持つ行ブロックをソートする方法は?

次のデータを含むファイルがあります。

BEGIN
hello2
5
world1
END
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END

ブロック内の数字に基づいて、次のように行をソートしたいと思います。この数字は別々でユニークです。

BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END

私はsedとawkを使ってブロックを印刷する方法を知っています。それはすべてです。

    # Prints the blocks including the BEGIN and END tags
    cat file | sed -n '/^BEGIN$/,/^END$/p'

    # Prints the blocks exluding the BEGIN and END tags
    awk '/^BEGIN$/ {show=1;next} /^END$/{show=0}  { print }' file

答え1

GNU awkを使う:

gawk '
    BEGIN { RS="\nEND\n"; ORS = RS; FS = "\n" }
    { record[$3] = $0 }
    END {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (val in record) print record[val]
    }
' file

あなたのデータによると、BEGINと数字の間には常に1行しかないと仮定しています。

このPROCINFO行は、「records」配列を繰り返す方法を定義します。バラよりhttps://www.gnu.org/software/gawk/manual/html_node/Controlling-Scanning.html

答え2

行が表示されるたびに、BEGIN別々のハンドルを使用してファイル内の次の数値行を個別に読み込みますgetline。 2 つのプレフィックス、つまり以前に取得した値を使用して、ファイルの各行を印刷します。そして現在のレコードのファイルレコード番号です(したがって、同じブロック内のすべての行は、BEGIN .. ENDブロック内に含まれる番号に対応するプレフィックス1の同じ値を持ちます)。プレフィックスを削除してプレフィックスベースの順序を処理するために、外部プログラムとsortユーティリティプログラムにこれを提供します。cut

awk '/BEGIN/{"awk \\$0+0==\\$0 "FILENAME | getline x}
{print x"~"FNR"~"$0 | "sort -k1,1n -k2,2n -t~ | cut -f3- -d~"}' file
BEGIN
hello4
2
world5
END
BEGIN
hello6
4
END
BEGIN
hello2
5
world1
END

答え3

最初の行は、テキストブロックを1行ずつ集計し、後続のソート基準として使用する数値を見つけることを試みます。 if 句if($0+0==$0)が数値を見つけた場合、その値は true です。

2 番目のブロックは、入力で「END」が見つかると実行されるため、ブロックを連想配列に格納し、ブロックで見つかった番号にインデックスを付けます。

awk '{block=block"\n"$0; if($0+0==$0) num=$0;} 
/^END$/ {blks[num]=block; block=""} 
END {for(key in blks) print blks[key]}' file

最後の行は、入力ファイルの終わりに達すると、配列内の各項目を印刷します。連想配列はすでにソートされているので(内部で機能する方法)、これを繰り返して各項目を印刷するだけです。

たとえば、次のawkスクリプトを確認してください。

echo | awk '{a[2]="b"; a[1]="a"; a[3]="c"; for(key in a) print a[key];}'

次のように出力されます。

a
b
c

私の答えでは、各ブロックの前に追加の項目を印刷するのは\n問題ではないようです。例の出力は次のとおりです。

BEGIN
hello4
2
world5
END

BEGIN
hello6
4
END

BEGIN
hello2
5
world1
END

追加の行が必要ない場合は、awkスクリプトの最初のブロックを次に置き換えます。

{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}}

1行のバージョンは次のとおりです。

awk '{if(length(block)=="0")block=$0; else{block=block"\n"$0; if($0+0==$0) num=$0}} /^END$/ {blks[num]=block; block=""} END {for(key in blks) print blks[key]}' file

関連情報