1. 概要

Linuxでは、テキストファイルの行を再配置するのが一般的な操作です。 場合によっては、特定の必要な順序で行を並べ替えたいことがあります。 sortコマンドはそれを助けることができます。

ただし、ファイル内の行をランダム化したい場合、つまり、ファイル内の行をシャッフルしたい場合があります。

このチュートリアルでは、テキストの行をシャッフルするさまざまな方法を見ていきます。 また、これらのアプローチを比較し、長所と短所について説明します。

2. 入力ファイルの例

ファイルのシャッフルを開始する前に、後のセクションのすべての例の入力として、最初にテキストファイルを準備しましょう。

$ cat input.txt
The original line number: 1
The original line number: 2
The original line number: 3
The original line number: 4
The original line number: 5
The original line number: 6
The original line number: 7
A line with some text.
A line with some text.
A line with some text.

上記の出力が示すように、 input.txt、という10行のファイルを作成しました。 最初の7行には、シャッフル後の結果を簡単に確認できるように、各行に元の行番号を入れています。

最後の3行が重複していること、つまり同じテキストが含まれていることは言及する価値があります。 シャッフルされた結果で繰り返される線の分布を観察したいので、これらの3本の線を追加しました。

このチュートリアルでは、3つの方法で入力ファイルをシャッフルしましょう。

  • shufコマンドの使用
  • sortコマンドの使用
  • 乱数ジェネレーターとsortコマンドの使用

3. shufコマンドの使用

shuf ユーティリティは、 GNUCoreutilsパッケージのメンバーです。 入力行のランダム順列を出力します。

shuf コマンドは、シャッフル中にすべての入力データをメモリにロードします。入力ファイルが空きメモリよりも大きい場合は機能しません。

次のコマンドを使用して、ファイル内の行をシャッフルするのは非常に簡単です。

$ shuf input.txt
The original line number: 7
The original line number: 4
A line with some text.
The original line number: 1
A line with some text.
The original line number: 5
A line with some text.
The original line number: 3
The original line number: 6
The original line number: 2

上記の出力は、同じテキストを含む3行を含め、行がランダムに並べ替えられていることを示しています。

コマンドを複数回実行すると、毎回異なる結果が得られます。

4. sortコマンドの使用

ほとんどの場合、 sort コマンドを使用して、ファイル内の行を特定の事前定義された順序で再配置します。 ただし、 sortコマンドをオプション-Rと一緒に使用して、ランダムソートを実行できます。

$ sort -R input.txt
A line with some text.
A line with some text.
A line with some text.
The original line number: 3
The original line number: 6
The original line number: 1
The original line number: 2
The original line number: 5
The original line number: 4
The original line number: 7

上記の出力は、予想どおり、線がランダムに再配置されていることを示しています。 しかし、同じテキストの3行をチェックすると、それらが連続していることがわかります

まあ、それはおそらく偶然に起こったのでしょう。 コマンドをもう一度実行して、何が起こるかを見てみましょう。

$ sort -R input.txt
The original line number: 6
The original line number: 5
A line with some text.
A line with some text.
A line with some text.
The original line number: 7
The original line number: 1
The original line number: 4
The original line number: 3
The original line number: 2

今回は、出力の行の順序が最初の実行とは異なります。 ただし、同じテキストの3行はまだ一緒です

実際、コマンドを何度実行しても、これらの3行は常に出力で連続しています。 それの訳は sortコマンドは、各行のハッシュキーを計算してから、生成されたハッシュキーでソートします。 入力行が同じである場合、同じハッシュ関数も同じハッシュキーを生成します。 したがって、3行は常に出力に含まれます。

5. 3ステップのアプローチ

shufsort-Rに加えて、ファイル内の行を自分でシャッフルすることもできます。 タイトルが示すように、3つのステップでそれを行います。

  1. 各行のプレフィックスとしてランダムな番号を追加します。
  2. プレフィックス番号で行を並べ替えます。
  3. プレフィックスを削除します。

5.1. ランダム番号を取得する

Linuxでランダムな数値を生成することは、興味深く、比較的複雑なトピックです。 このチュートリアルでは、乱数生成分析については詳しく説明しません。 代わりに、Linuxでランダムな数値を取得するいくつかの方法を紹介します。

Linuxでランダムな数値を取得する最も簡単な方法は、 $RANDOMの「変数」を読み取ることです。

$ echo $RANDOM 
12587
$ echo $RANDOM
26089
$ echo $RANDOM
17442

実際、 $ RANDOMは、ランダムに符号付けされた16ビット整数(0〜32767)を生成する内部Bash関数です。

乱数を取得する別の方法は、デバイス / dev /randomを使用することです。

/ dev /randomは特別なデバイスファイルです。 デバイスドライバやその他のソースから収集されたノイズを使用して、ランダムデータを生成します。 od (octal dump)コマンドを使用して、バイト数を抽出し、それに相当する10進数を出力できます。

$ od -An -N2 -i /dev/random
   25824
$ od -An -N2 -i /dev/random
   56167
$ od -An -N2 -i /dev/random
   42527

/ dev / randomは、カーネルの乱数ジェネレーターでもあります。 4096ビットのランダムな整数を生成できます

$ cat /proc/sys/kernel/random/poolsize
4096

Python、Perl、awkなどの高レベルのスクリプト言語でランダムな数値を取得することもできます。

$ awk 'BEGIN{srand();for(i=1;i<=3;i++)print rand()}'
0.714734
0.174336
0.369674

5.2. コマンドを組み立てる

最後に、次のようなコマンドを作成する必要があります。

cmd-to-prepend-random-number-on-each-line | sort -n -k 1 | cmd-to-remove-random-number-prefix-from-each-line

中央のsortコマンドは、準備された行を最初のフィールドでソートします。このフィールドには、追加されたランダムな数値が含まれています。

強力なawkコマンドを使用して、各行にランダムな数字を追加してみましょう。

$ awk 'BEGIN{srand()}{print rand(), $0}' input.txt
0.118435 The original line number: 1
0.277674 The original line number: 2
0.139113 The original line number: 3
0.351707 The original line number: 4
0.178648 The original line number: 5
0.128693 The original line number: 6
0.625488 The original line number: 7
0.179445 A line with some text.
0.100277 A line with some text.
0.584702 A line with some text.

ランダムな数字のプレフィックスを削除するには、各行の先頭から最初のスペース(両端を含む)までのテキストを削除します。 それを行うには多くのオプションがあります。 引き続きawkコマンドを使用してジョブを実行します。

それでは、3つの部分を一緒に構築しましょう。

$ awk 'BEGIN{srand()}{print rand(), $0}' input.txt \
    | sort -n -k 1 \
    | awk 'sub(/\S* /,"")'
The original line number: 4
A line with some text.
The original line number: 1
The original line number: 2
A line with some text.
The original line number: 7
A line with some text.
The original line number: 6
The original line number: 3
The original line number: 5

上記の結果は、重複した3行を含むすべての行がランダムに再配置されていることを示しています。

6. パフォーマンスの比較

これまで、ファイル内の行をシャッフルする3つの異なる方法を見てきました。 どちらが速いのかと尋ねるかもしれません。

まず、パフォーマンステスト用にseqコマンドを使用して大きなファイルを作成しましょう。

$ seq -f "The original line number: %g" 3000000 > big.txt

$ ls -l big.txt 
-rw-r--r-- 1 kent kent 104M May 23 23:06 big.txt

$ wc -l big.txt 
3000000 big.txt

上記の出力が示すように、big.txtという名前のファイルを作成しました。このファイルには300万行が含まれています。

次に、 time コマンドを使用して、さまざまなアプローチのパフォーマンスをテスト比較します。

まず、shufコマンドをテストしてみましょう。

$ time shuf big.txt > result.txt
real 0.73
user 0.64
sys 0.08

そして今、 sort -Rアプローチ:

$ time sort -R big.txt > result.txt                      
real 35.73
user 117.17
sys 0.17

そして最後に、私たちの3つのステップのアプローチ:

$  time awk 'BEGIN{srand()}{print rand(), $0}' big.txt \  
    | sort -n -k 1 \                                          
    | awk 'sub(/\S* /,"")' > result.txt
real 2.99                              
user 1.67
sys 0.05

結果は、 sort -Rアプローチがshufを使用したアプローチよりもはるかに(約50倍)遅いことを示しています。 これは、 sort コマンドでは1行ごとにハッシュキーを計算する必要があるためです。これは非常にコストのかかる操作ですが、shufコマンドではこの計算が行われません。 さらに、shufはメモリ内のすべての作業を実行します。

私たちの3ステップのソリューションもかなり高速です。 ただし、それでもshufアプローチよりも約4倍遅くなります。 これは、3ステップのアプローチが3つのプロセスを開始し、大きな入力を3回読み取るためです。

7. 長所と短所

パフォーマンステストの後、これら3つのアプローチの長所と短所について説明します。

7.1. shufコマンド

長所:

  • 簡単
  • すべての処理がメモリ内で行われるため、高速です
  • 重複した行もシャッフルできます

短所:

  • 空きメモリの量に制限されたファイルサイズ

ファイルをシャッフルする必要があり、ファイルをメモリにロードできる場合は、shufコマンドが最初の選択肢になります。

ただし、 shuf コマンドを使用して、メモリサイズよりも大きいサイズの巨大なファイルの行をシャッフルする場合は、最初に分割して小さなファイルにし、次に、シャッフル後にそれらをマージします。

7.2. ソート-Rアプローチ

長所:

  • 簡単
  • ファイルサイズに制限はありません

短所:

  • ハッシュ計算メカニズムのために大きなファイルで遅くなる
  • 重複した線は常にくっつきます

7.3. 3段階のアプローチ

長所:

  • 適度に速い
  • 重複した行もシャッフルできます
  • ファイルサイズに制限はありません
  • 柔軟で拡張可能

短所:

  • スクリプトは他のソリューションよりも複雑です
  • 3つのプロセスを開始し、入力データを3回処理します

メモリに完全にロードできない巨大なファイルをシャッフルする必要がある場合、このアプローチは shuf コマンドよりも単純で、 sort-Rアプローチよりもはるかに高速です。

8. 結論

この記事では、ファイルの行をシャッフルする3つの異なる方法を学びました。 また、パフォーマンスを比較し、長所と短所について説明しました。

それとは別に、Linuxでもランダムな数値を取得する方法を見てきました。