sedを使用してテキストファイルから複数のランダムな行を削除する方法は?

sedを使用してテキストファイルから複数のランダムな行を削除する方法は?

90行のテキストファイルから任意の10行を削除して新しいファイルに出力したいです。私はこの作業にsedを使用しようとしましたが、2つの問題があります。私は以下を使用しています:

sed -i $((1 + RANDOM & 90))d input.txt > output.txt

その後、コマンドを10回実行します(より良い方法があるようです!)。

私が経験する最初の問題は、エラーが発生することです。

sed: -e 式 #1, char 2: ラインアドレス 0 の使用が無効です。

私はこれが行1を削除して再試行しているという事実に関連していると思います。

2番目の問題は、同じコマンドを使用する前に動作していましたが、出力ファイルに何も書き込まれない場合があることです。

答え1

RANDOM % 90代わりに使用したいかもしれません&。ここで0が出ます(行1を削除すると、次回の実行時に行番号が1..89に指定されます)。

しかし、問題があります。数式は同じ数を複数回生成できます。これを防ぐには、別のアプローチを使用してください。つまり、数字を混ぜて上位10個を選択します。

shuf -i1-90 -n10 | sed 's/$/d/' | sed -f- input > output

sed生成スクリプトが気に入らない場合は、以下をsed使用することもできますprintf

sed -f <( printf %dd\;  $(shuf -i1-90 -n10) ) input > output

答え2

移植可能なGNUがない場合は、shuf次のようにできます。

awk -v n=90 -v p=10 '
  BEGIN {srand()}
  rand() * n-- < p {p--; next}
  {print}' < file

pまた、shuf + sedはo(n)にあり、shuf + sedはo(n * p)にあるため、高値のshuf + sedメソッドよりも効率的です。 n = 1000000の場合、私のシステムのブレークポイントは、GNU sed対GNU awkの場合は約p = 35で、GNU sed対mawkの場合はp = 1程度です(mawkでは常に高速です)。

答え3

ここでの課題は、90行の1つを削除してから、残りの89行の1つを削除することです。 89行だけが残っていると、90行目を削除できません。

eval $(for i in {90..81}; do CMD="$CMD | sed $(( (RANDOM % $i)+1 ))d"; done; echo cat infile $CMD) > outfile

forループは一連の文字列を累積してパイプラインを形成します。ここで| sed NNdNN は 1 から 90 から始まり、1 から 81 まで終わる縮小範囲の乱数で、結果は次のようになります.| sed 88d | sed 12d | sed 36d...

CMDコマンドが形成されたら、それをcat infileパイプラインCMDの前に追加します(CMDは|forループでaで始まります)。 CMDは次のようになりますcat infile | sed 88d | sed 12d...

最後に、evalコマンドのCMD文字列を実行して結果をoutfile

答え4

パフォーマンスに問題がない場合は、次のものを使用できます。

cat PATH_OF_SOURCE_FILE | \
grep -n ^ | \
grep -E "^($(seq 1 90 | shuf | head -n 80 | paste -s -d '|')):" | \
sed 's/[0-9]*:\(.*\)$/\1/' > PATH_TO_TARGET_FILE

1つ目はgrep行をインデックス化し、2つ目はgrep80行をランダムに選択し、sed最初に追加された行番号を削除しますgrep

shuf注:出力順序が必要ない場合は、最後の出力をパイプしてください。

関連情報