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 NNd
NN は 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つ目はgrep
80行をランダムに選択し、sed
最初に追加された行番号を削除しますgrep
。
shuf
注:出力順序が必要ない場合は、最後の出力をパイプしてください。