マルチラインファイルランダム再生

マルチラインファイルランダム再生

テキストブロックを区切る空白行のテキストファイルがあります。 * NIXコマンドラインツールを使用してブロック構造を尊重しながら、このファイルを混在させたいと思います。つまり、出力でブロックの順序が変更されていることを確認し、ブロック内の行とその順序が同じであることを確認したいと思います。

入力ファイルの例:

line 1
line 2

line 10
line 20
line 30

line 100
line 200

出力ファイル(シャフリング後):

line 10
line 20
line 30

line 1
line 2

line 100
line 200

もちろん、繰り返し実行は異なるブロック順序を提供する必要があります。

ファイルの最初の行は常に空ではありません。二重空行はありません。ファイルの最後の行は常に空です。

私はリストリストのすべての行を読み、それを混ぜて出力する非常に単純なPythonスクリプトを書いています。標準* NIXツールを使用してこれを実行できるかどうか疑問に思います。

答え1

POSIXlyでは、次のことができます。

<file awk '
  BEGIN{srand(); n=rand()}
  {print n, NR, $0}
  !NF {n=rand()}
  END {if (NF) print n, NR+1, ""}' |
  sort -nk1 -k2 |
  cut -d' ' -f3-

つまり、各行の前に<a-random-number-that-changes-with-each-paragraph>行番号を追加し、最初の番号を数字で並べ替え、2番目の番号を並べ替えて段落の行順を維持し、関連性のない番号を削除します。

末尾の空白行を削除するにはパイプが必要な場合がありますsed '$d'

ほとんどのawk実装では、 srand()Unixのエポック時間を使用して擬似乱数ジェネレータをシードするため、同じ秒以内に2回実行すると同じ結果が得られます。残念ながら私の努力にもかかわらず、POSIX仕様に歴史的なエラーが刻まれています。)。

答え2

GNU ツールを使用すると、段落を NUL で区切られたグループに分割し、混合して NUL を削除します。

$ sed '1s/^/\n/; s/^$/\x00/' input | shuf -z | sed '1d; s/\x00//'
line 100
line 200

line 10
line 20
line 30

line 1
line 2

NULを使用しない代替方法

すべてのツールがNUL文字をサポートしているわけではないので、以下は代替手段です。これは段落を読み、~改行文字を置き換え、混ぜ、~結果を表示する前に改行文字に変換します。

$ awk '{gsub(/\n/, "~")} 1' RS= input | shuf | awk '{gsub(/~/, "\n")} 1' ORS="\n\n"
line 10
line 20
line 30

line 100
line 200

line 1
line 2

テキストに文字を含めることができる場合は、~テキストに含まれていない他の文字を一時行区切り文字として使用してください。

答え3

パールの使用:

perl -MList::Util -00 -e 'chomp(my @a=<>); print join("\n\n", List::Util::shuffle @a) . "\n";' < input

またはスクリプトファイルに展開します。

#!/usr/bin/perl
use List::Util 'shuffle';
local $/ = "";  ## paragraph mode
chomp(my @a = <>);
print join("\n\n", shuffle @a) . "\n";

関連情報