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


285

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

どのように私はそれを行うことができcatawkcut、など?



うん、その元の質問にもいくつか他の良い答えがあります。
Ruggiero Spearman

だから、あなたはWPAワードリストを作っていましたか?(単なるランダムな推測)
thahgr '19年

回答:


360

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

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

[編集者注:重複した行/ソートキーが常に隣同士になることを除いて、sort -R ほとんどシャッフルします。言い換えれば、ユニークな入力行/キーだけで、それは本当のシャッフルです。出力順序がハッシュ値によって決定されることは事実ですが、ランダム性はランダムハッシュ関数を選択することから生じますマニュアルを参照してください。]


31
shufそして、sort -Rするので、若干異なるsort -Rランダムに従って要素を順序付けハッシュそれらのある、sort -R一方で、一緒に繰り返し要素を置くshufシャッフルランダムにすべての要素。
SeMeKh 2012

146
OS Xユーザーの場合:brew install coreutils、次にgshuf ...(:
ELLIOTTCABLE

15
sort -Rそして、shufは完全に異なると見なされるべきです。sort -R確定的です。同じ入力で異なる時間に2回呼び出すと、同じ答えが返されます。shuf一方、はランダムな出力を生成するため、同じ入力で異なる出力が得られる可能性が高くなります。
EfForEffort 2013

18
不正解です。「sort -R」は、起動するたびに異なるランダムハッシュキーを使用するため、毎回異なる出力を生成します。
Mark Pettit、2014年

3
ランダム性に関する注意:GNUドキュメントによれば、「デフォルトでは、これらのコマンドは少量のエントロピーによって初期化された内部疑似ランダムジェネレーターを使用しますが、-random-source = fileオプションで外部ソースを使用するように指示できます。」
Royce Williams

85

Perlワンライナーはマキシムのソリューションの単純なバージョンになります

perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile

6
OS Xでシャッフルするためにこれをエイリアスしました。ありがとう!
Unfun Cat

これは、このページでREALランダム行を返す唯一のスクリプトでした。他のawkソリューションは、しばしば重複した出力を印刷しました。
フェリペアルバレス

1
しかし、アウトでは1行が失われるので注意してください:)それは別の行と結合されます:)
JavaRunner

@JavaRunner:末尾のない入力について話していると思います\n。はい、それ\nは存在しなければなりません-そしてそれは通常あり ます-そうでなければあなたはあなたが説明するものを得るでしょう
mklement0

1
素晴らしく簡潔です。私は交換することをお勧め<STDIN>して<>解決策はからの入力で動作しますので、ファイルすぎ。
mklement0

60

この回答は、既存の多くの回答を次のように補完します。

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

    • 関数stdin入力だけでなく、ファイル名引数も受け取ります
    • 関数は、騒々しく壊れるのではなく、通常の方法(終了コードによる静かな終了)で処理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))
' "$@"; }

この関数のWindowsバージョンについては、下のセクションを参照してください。

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使用する実装によって異なります(たとえば、awkOSXで使用されるBSD バージョンは、通常、GNUよりも遅くawk、特にmawk)。これにより、相対的なパフォーマンスの一般的な感覚が得られます

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

  • shuf
    • 0.090s
  • Ruby 2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • パイソン
    • 1.342sPython 2.7.6; 2.407s(!)Python 3.4.2
  • awk+ sort+cut
    • 3.003sBSDでawk; 2.388sGNU awk(4.1.1); (1.3.4)。1.811smawk

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

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

結論

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

PythonソリューションのWindowsバージョン(Pythonコードは同じですが、引用のバリエーションと、Windowsでサポートされていない信号関連ステートメントの削除を除きます):

  • PowerShellの場合(Windows PowerShellでは$OutputEncoding、パイプライン経由で非ASCII文字を送信するかどうかを調整する必要があります):
# Call as `shuf someFile.txt` or `Get-Content someFile.txt | shuf`
function shuf {
  $Input | python -c @'
import sys, random, fileinput;
lines=[line for line in fileinput.input()];
random.shuffle(lines); sys.stdout.write(''.join(lines))
'@ $args  
}

PowerShellは、そのGet-Randomコマンドレットを介してネイティブにシャッフルできることに注意してください(パフォーマンスが問題になる可能性があります)。例えば:
Get-Content someFile.txt | Get-Random -Count ([int]::MaxValue)

  • cmd.exe(バッチファイル):

shuf.cmdたとえば、ファイルに保存します。

@echo off
python -c "import sys, random, fileinput; lines=[line for line in fileinput.input()]; random.shuffle(lines); sys.stdout.write(''.join(lines))" %*

私が代わりにこの単純なワンライナーを使用してSIGPIPEは、Windowsに存在しません:python -c "import sys, random; lines = [x for x in sys.stdin.read().splitlines()] ; random.shuffle(lines); print(\"\n\".join([line for line in lines]));"
eligが

@elig:ありがとうございます。ただしfrom signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL);、元のソリューションを省略しても十分であり、ファイル名引数を渡すこともできるという柔軟性を保持しています。他に何も変更する必要はありません(引用符を除く)-に追加した新しいセクションを参照してください底。
mklement0

27

私は「unsort」と呼ぶ小さなperlスクリプトを使用します。

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

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

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


RHEL 5.6にはshufがありません(
Maxim Egorushkin

1
よくできました。ソリューションをファイルからの入力でも機能させるために<STDIN><>で置き換えることをお勧めします
mklement0

20

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

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

8
UUOC。ファイルをawk自体に渡します。
ghostdog74 2010年

1
そう、私はでデバッグしhead myfile | awk ...ます。次に、それを猫に変更します。それがそこに残された理由です。
Ruggiero Spearman、2010年

-k1 -nawkの出力はrand()0と1の間の10進数であり、重要なのは何らかの方法で並べ替えられることだけなので、並べ替えの必要はありません。-k1rand()の出力は比較を短絡させるのに十分なほど一意である必要がありますが、行の残りを無視することでスピードアップに役立つ場合があります。
bonsaiviking 2014年

@ ghostdog74:catのほとんどのいわゆる役に立たない使用法は、パイプされたコマンド間で一貫性を保つために実際に役立ちます。各プログラムがファイル入力を受け取る(または行わない)方法を覚えるよりも、cat filename |(または< filename |)を保持する方が適切です。
ShreevatsaR 2014

2
shuf(){awk 'BEGIN {srand()} {print rand() "\ t" $ 0}' "$ @" | 並べ替え| cut -f2-;}
Meow

16

これが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

うまくできていますが、実際には、およびと組み合わせたOPの独自の回答よりもはるかに低速です。数千行以下の場合はそれほど大きな違いはありませんが、行数が多いほど重要です(しきい値は使用する実装によって異なります)。わずかな簡略化が行を置き換えることであろうとして。awksortcutawkwhile (1){if (e==d) {break}while (e<d)
mklement0

11

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を超える)要素ではうまく機能しません。


2
「欠点」はPythonに固有のものではありません。PRNGをシステムのようにエントロピーで再シードすることにより、有限のPRNG期間を回避できます/dev/urandom。Pythonから使用するには:random.SystemRandom().shuffle(L)
jfs

join()は '\ n'にある必要はないので、行はそれぞれ独自に印刷されますか?
elig

@elig:いいえ、末尾の改行含む.readLines()返します。
mklement0

9

単純な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数値を理解できないシステムでも正しく機能することに注意してください。これは、辞書式順序(別名通常の文字列比較)でソートできます。


OP自身の回答を関数としてパッケージ化するのは良い考えです。を追加すると"$@"、入力としてファイル使用することもできます。乗算する理由はありませんrand()ので、sort -n小数点以下の端数をソートすることができます。ただし、awkの出力形式を制御することをお勧めします。デフォルトの形式では%.6g指数表記でrand()時々の数が出力されるためです。実際には、最大100万行をシャッフルすることで間違いなく十分ですが、パフォーマンスを犠牲にすることなく、より多くの行をサポートすることは簡単です。例えば。%.17f
mklement0

1
@ mklement0私が書いている最中にOPの回答に気付かなかった。rand()は10e6で乗算され、私の知る限り、solarisまたはhpuxソートで機能します。"$ @"とグッドアイデア
のMichałŠrajer

1
了解、ありがとう; おそらく、乗算の根拠を答え自体に追加できます。一般に、POSIXによれば、sort小数部を処理できるはずです(私が気付いたように、何千ものセパレータがあっても)。
mklement0

7

Ruby FTW:

ls | ruby -e 'puts STDIN.readlines.shuffle'

1
素晴らしいもの; を使用するputs ARGF.readlines.shuffleと、stdin入力引数とファイル名引数の両方で機能させることができます。
mklement0

さらに短いruby -e 'puts $<.sort_by{rand}'— ARGFはすでに列挙可能であるため、ランダムな値で並べ替えることで行をシャッフルできます。
akuhn 2015

6

scaiの回答に基づく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

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

例:

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

the
an
linux
ubuntu
life
good
breeze

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

$ shuf words.txt

これは、シャッフルされた行を標準出力にスローします。したがって、次のように出力ファイルパイプする必要があります

$ shuf words.txt > shuffled_words.txt

そのようなシャッフル実行の 1つは、

breeze
the
linux
an
ubuntu
good
life

4

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

sudo apt-get install randomize-lines

例:

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

seq 1000 > 1000.txt

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

rl 1000.txt

3

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

#!/bin/python

import sys
import random

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

    for line in flist:
      print line.strip()

マックOSX上sort -Rshufあなたにあなたのbash_profileでこれを別名設定できるので、使用できません。

alias shuf='python rand.py'

3

私のようshufにmacOSの代替を探すためにここに来た場合は、を使用してください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

1
Coreutilsをインストールbrew install coreutilsすると、shufバイナリがとして提供されgshufます。
シャドウトーカー2017

2

Scalaがインストールされている場合、入力をシャッフルするためのワンライナーは次のとおりです。

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

驚くほど単純ですが、Java VMを起動する必要がない限り、その起動コストはかなり高くなります。行数が多い場合もうまく機能しません。
mklement0

1

このbash関数には最小限の依存関係があります(並べ替えとbashのみ)。

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

OP独自のawk支援型ソリューションに匹敵する優れたbashソリューションですが、入力が大きくなるとパフォーマンスが問題になります。単一の$RANDOM値を使用すると、最大32,768の入力行のみが正しくシャッフルされます。その範囲を拡張することはできますが、おそらく価値はありません。たとえば、私のマシンでは、32,768の短い入力行でスクリプトを実行すると、約1秒かかります。これは、実行shuf時間の約150倍 、約10〜15倍OP独自のawk支援ソリューションが必要である限り。あなたがsort存在することに頼ることができるawkなら、そこにもいるはずです。
mklement0

0

Windows では、data.txtをシャッフルするのに役立つこのバッチファイルを試すことができます。バッチコードの使用法は次のとおりです。

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

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

お役に立てれば。


大きなファイルには機能しません。2時間後に100万行以上のファイルをあきらめました
Stefan Haberl 2014

0

現時点では言及されていません:

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

    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

別の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];
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.