web-dev-qa-db-ja.com

Unixコマンドラインまたはシェルスクリプトでテキストファイルの行をシャッフルするにはどうすればよいですか?

テキストファイルの行をランダムにシャッフルし、新しいファイルを作成します。ファイルには数千行ある場合があります。

catawkcutなどでどうすればよいですか?

264

shuf を使用できます。少なくとも一部のシステムでは(POSIXにはないようです)。

Jleedevが指摘したように:sort -Rもオプションかもしれません。少なくとも一部のシステムでは。まあ、あなたは写真を取得します。 指摘されているsort -Rは実際にはシャッフルせず、代わりにハッシュ値に従ってアイテムをソートします。

[編集者注:sort -Ralmostシャッフル、ただしduplicate行/ソートキーは常に互いに隣り合っています。言い換えると、unique入力行/キーを使用した場合のみ、真のシャッフルになります。出力順序がハッシュ値によって決定されるのは事実ですが、ランダム性はランダムハッシュfunction- 手動 を参照してください。]

334
Joey

Perlのワンライナーは、マキシムのソリューションのシンプルなバージョンです

Perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
80
Moonyoung Kang

この回答は、次の方法で多くの素晴らしい既存の回答を補完します。

  • 既存の回答は、柔軟なシェル関数にパッケージ化されています

    • 関数stdin入力だけでなく、filename引数も取ります
    • 関数は、SIGPIPEを通常の方法で処理するための追加の手順を実行します(終了コード141での静かな終了)。これは、headにパイプするときなど、関数出力を早期に閉じられたパイプにパイプするときに重要です。
  • パフォーマンス比較が行われます。


  • POSIX準拠awksort、およびcutに基づく関数、適応 OP自身の回答 から:
shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print Rand(), $0}' "$@" |
               sort -k1,1n | cut -d ' ' -f2-; }
shuf() { Perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
shuf() { python -c '
import sys, random, fileinput; from signal import signal, SIGPIPE, SIG_DFL;    
signal(SIGPIPE, SIG_DFL); lines=[line for line in fileinput.input()];   
random.shuffle(lines); sys.stdout.write("".join(lines))
' "$@"; }
shuf() { Ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT");
                     puts ARGF.readlines.shuffle' "$@"; }

性能比較:

注:これらの数値は、OSX 10.10.3を実行する3.2 GHz Intel Core i5とFusion Driveを搭載した2012年後半のiMacで取得されたものです。 タイミングは、使用するOS、マシン仕様、使用するawk実装によって異なりますが(たとえば、OSXで使用されるBSD awkバージョンは通常、GNU awk、特にmawk)、これは、relativeパフォーマンスの一般的な意味を提供する必要があります。

Inputファイルは、seq -f 'line %.0f' 1000000で生成された100万行のファイルです。
時間は昇順でリストされます(最速が最初):

  • shuf
    • 0.090s
  • ルビー2.0.0
    • 0.289s
  • Perl5.18.2
    • 0.589s
  • Python
    • 1.342sとPython 2.7.6; 2.407s(!)とPython 3.4.2
  • awk + sort + cut
    • 3.003s with BSD awk; 2.388s with GNU awk(4.1.1); mawkを含む1.811s(1.3.4);

さらに比較するために、上記の関数としてパッケージ化されていないソリューション:

  • sort -R(重複する入力行がある場合、真のシャッフルではありません)
    • 10.661s-より多くのメモリを割り当てても違いはないようです
  • Scala
    • 24.229s
  • bash LOOPS + sort
    • 32.593s

結論

  • 可能な場合はshufを使用してください-最速です。
  • Rubyがうまくいき、その後にPerlが続きます。
  • Pythonは、RubyやPerlよりも著しく遅く、Pythonバージョンを比較すると、2.7.6は3.4.1よりもかなり高速です。
  • POSIX準拠のawk + sort + cutコンボを最後の手段として使用する;どのawk実装を使用するかは重要です(mawkはGNU awkより高速ですが、BSD awkは最も低速です)。
  • sort -Rbashループ、およびScalaには近づかないでください。
56
mklement0

私は小さなPerlスクリプトを使用します。これを「ソート解除」と呼びます。

#!/usr/bin/Perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);

また、「unsort0」と呼ばれるNULL区切りバージョンもあります。find-print0などで使用すると便利です。

PS:「shuf」にも投票しましたが、最近coreutilsにあるとは思いもしませんでした。システムに「shuf」がない場合、上記はまだ役に立つかもしれません。

27
NickZoic

コーダーでは簡単ですが、各行に乱数を追加し、並べ替えてから各行から乱数を削除するCPUでは難しい最初の試行です。実際には、行はランダムにソートされます。

cat myfile | awk 'BEGIN{srand();}{print Rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
19

ここにawkスクリプトがあります

awk 'BEGIN{srand() }
{ lines[++d]=$0 }
END{
    while (1){
    if (e==d) {break}
        RANDOM = int(1 + Rand() * d)
        if ( RANDOM in lines  ){
            print lines[RANDOM]
            delete lines[RANDOM]
            ++e
        }
    }
}' file

出力

$ cat file
1
2
3
4
5
6
7
8
9
10

$ ./Shell.sh
7
5
10
9
6
8
2
1
3
4
16
ghostdog74

Pythonのワンライナー:

python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," myFile

ランダムな1行だけを印刷するには:

python -c "import random, sys; print random.choice(open(sys.argv[1]).readlines())," myFile

しかし、Pythonのrandom.shuffle()の欠点については この投稿 を参照してください。多くの(2080を超える)要素ではうまく機能しません。

11
scai

シンプルなawkベースの関数が仕事をします:

shuffle() { 
    awk 'BEGIN{srand();} {printf "%06d %s\n", Rand()*1000000, $0;}' | sort -n | cut -c8-
}

使用法:

any_command | shuffle

これは、ほとんどすべてのUNIXで動作するはずです。 Linux、Solaris、およびHP-UXでテスト済み。

更新:

先行ゼロ(%06d)およびRand()乗算により、sortが数値を認識しないシステムでも適切に動作することに注意してください。辞書順で並べ替えることができます(別名、通常の文字列比較)。

9
Michał Šrajer

ルビーFTW:

ls | Ruby -e 'puts STDIN.readlines.shuffle'
7
hoffmanc

scai's answer に基づくPythonの1つのライナー、ただしa)stdinを使用し、b)結果をシードで繰り返し可能にし、c)すべての行のうち200行のみを選択します。

$ cat file | python -c "import random, sys; 
  random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
  > 200lines.txt
6
dfrankow

シンプルで直感的な方法は、shufを使用することです。

例:

words.txtを次のように仮定します。

the
an
linux
ubuntu
life
good
breeze

行をシャッフルするには、次のようにします。

$ shuf words.txt

シャッフルされた行を標準出力にスローします;だから、あなたはパイプにそれを出力ファイルのように:

$ shuf words.txt > shuffled_words.txt

そのようなshuffle runの結果は次のようになります。

breeze
the
linux
an
ubuntu
good
life
5
kmario23

まさにその仕事をするパッケージがあります:

Sudo apt-get install randomize-lines

例:

番号の順序付きリストを作成し、1000.txtに保存します。

seq 1000 > 1000.txt

シャッフルするには、単に使用します

rl 1000.txt
4
navigaid

私のように、macOS用のshufの代替を探すためにここに来たら、randomize-linesを使用してください。

randomize-lines(homebrew)パッケージをインストールします。このパッケージには、rlと同様の機能を持つshufコマンドがあります。

brew install randomize-lines

Usage: rl [OPTION]... [FILE]...
Randomize the lines of a file (or stdin).

  -c, --count=N  select N lines from the file
  -r, --reselect lines may be selected multiple times
  -o, --output=FILE
                 send output to file
  -d, --delimiter=DELIM
                 specify line delimiter (one character)
  -0, --null     set line delimiter to null character
                 (useful with find -print0)
  -n, --line-number
                 print line number with output lines
  -q, --quiet, --silent
                 do not output any errors or warnings
  -h, --help     display this help and exit
  -V, --version  output version information and exit
3
Ahmad Awais

これは、ホームフォルダーにRand.pyとして保存したpythonスクリプトです。

#!/bin/python

import sys
import random

if __== '__main__':
  with open(sys.argv[1], 'r') as f:
    flist = f.readlines()
    random.shuffle(flist)

    for line in flist:
      print line.strip()

Mac OSXではsort -Rshufは使用できないため、bash_profileで次のようにエイリアスできます。

alias shuf='python Rand.py'
3
Jeff Wu

Scalaがインストールされている場合、入力をシャッフルするための1つのライナーがあります。

ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
2
swartzrock

このbash関数には最小限の依存関係があります(sortとbashのみ):

shuf() {
while read -r x;do
    echo $RANDOM$'\x1f'$x
done | sort |
while IFS=$'\x1f' read -r x y;do
    echo $y
done
}
1
Meow

まだ言及されていない:

  1. unsort util。構文(ややプレイリスト指向):

    unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
           [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] 
           [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] 
           [--linefeed] [file ...]
    
  2. msort は行単位でシャッフルできますが、通常は過剰です:

    seq 10 | msort -jq -b -l -n 1 -c r
    
0
agc

別のawkバリアント:

#!/usr/bin/awk -f
# usage:
# awk -f randomize_lines.awk lines.txt
# usage after "chmod +x randomize_lines.awk":
# randomize_lines.awk lines.txt

BEGIN {
  FS = "\n";
  srand();
}

{
  lines[ Rand()] = $0;
}

END {
  for( k in lines ){
    print lines[k];
  }
}
0
biziclop

Windowsでは、 このバッチファイル を試して、data.txtをシャッフルすることができます。バッチコードの使用方法は

C:\> type list.txt | shuffle.bat > maclist_temp.txt

このコマンドを発行すると、maclist_temp.txtにはランダム化された行のリストが含まれます。

お役に立てれば。

0
Ayfan