318

我想随机打乱文本文件的行并创建一个新文件。该文件可能有数千行。

我怎么能用cat, awk,cut等来做到这一点?

4

19 回答 19

398

您可以使用shuf. 至少在某些系统上(似乎不在 POSIX 中)。

正如 jleedev 指出的那样:sort -R也可能是一种选择。至少在某些系统上;好吧,你明白了。有人指出,sort -R并没有真正洗牌,而是根据它们的哈希值对项目进行排序。

[编者注:sort -R 几乎是洗牌,除了重复的行/排序键总是彼此相邻。换句话说:只有唯一的输入行/键才是真正的随机播放。虽然输出顺序确实由哈希值决定,但随机性来自选择随机哈希函数- 请参阅手册。]

于 2010-01-28T10:51:34.550 回答
90

Perl one-liner 将是 Maxim 解决方案的简单版本

perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
于 2011-06-28T18:28:16.240 回答
68

该答案通过以下方式补充了许多现有的答案:

  • 现有答案被打包成灵活的shell 函数

    • 这些函数不仅接受stdin输入,还接受文件名参数
    • 这些函数采取额外的步骤以SIGPIPE通常的方式处理(使用退出代码安静终止141),而不是吵闹。这在将函数输出通过管道传输到提前关闭的管道时很重要,例如当管道传输到head.
  • 进行了性能比较


  • 基于 、 和 的 POSIX 兼容函数awksort改编cutOP自己的答案
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' "$@"; }

性能对比:

注意:这些数字是在 2012 年末的 iMac 上获得的,该 iMac 配备 3.2 GHz Intel Core i5 和 Fusion Drive,运行 OSX 10.10.3。虽然时间会随着使用的操作系统、机器规格、awk使用的实现而变化(例如,在 OSX 上使用的 BSDawk版本通常比 GNU 慢awk,尤其是mawk),但这应该提供相对性能的一般感觉。

输入文件是一个100 万行的文件,使用seq -f 'line %.0f' 1000000.
时间按升序排列(最快的在前):

  • shuf
    • 0.090s
  • 红宝石2.0.0
    • 0.289s
  • Perl 5.18.2
    • 0.589s
  • Python
    • 1.342s使用 Python 2.7.6;2.407s(!) 使用 Python 3.4.2
  • awk+ sort+cut
    • 3.003s与 BSD awk; 2.388s使用 GNU awk(4.1.1);1.811smawk(1.3.4);

为了进一步比较,未打包为上述功能的解决方案:

  • sort -R(如果有重复的输入行,则不是真正的随机播放)
    • 10.661s- 分配更多内存似乎没有什么区别
  • 斯卡拉
    • 24.229s
  • bash循环 +sort
    • 32.593s

结论

  • shuf如果可以,请使用- 这是迄今为止最快的。
  • Ruby做得很好,其次是Perl
  • Python明显比 Ruby 和 Perl 慢,并且比较 Python 版本,2.7.6 比 3.4.1 快很多
  • 使用符合 POSIX 标准的awk++sort组合cut作为最后的手段;您使用哪种awk实现很重要(mawk比 GNU 快awk,BSDawk最慢)。
  • 远离sort -Rbash循环和 Scala。

Windows版本的Python解决方案(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-Randomcmdlet 本地随机播放(尽管性能可能是个问题);例如:
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))" %*
于 2015-05-08T21:41:50.743 回答
27

我使用一个很小的 ​​perl 脚本,我称之为“unsort”:

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

我还有一个以 NULL 分隔的版本,称为“unsort0”……方便与 find -print0 等一起使用。

PS:也投了“shuf”的票,我不知道这些天在 coreutils 中存在……如果您的系统没有“shuf”,上述内容可能仍然有用。

于 2010-01-28T13:10:13.203 回答
22

这是第一次尝试,它在编码器上很容易,但在 CPU 上却很难,它为每行添加一个随机数,对它们进行排序,然后从每行中去除随机数。实际上,这些行是随机排序的:

cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
于 2010-01-28T10:50:47.340 回答
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
于 2010-01-28T11:29:35.253 回答
11

python的单线:

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

并且只打印一条随机线:

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

但请参阅这篇文章了解 python 的缺点random.shuffle()。它不适用于许多(超过 2080 个)元素。

于 2013-07-11T07:29:56.990 回答
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不理解数字的系统上也能正常工作。它可以通过字典顺序(又名普通字符串比较)进行排序。

于 2011-10-12T18:24:30.203 回答
8

红宝石 FTW:

ls | ruby -e 'puts STDIN.readlines.shuffle'
于 2014-12-16T16:43:36.407 回答
7

一种简单直观的方法是使用shuf.

例子:

假设words.txt

the
an
linux
ubuntu
life
good
breeze

要洗牌,请执行以下操作:

$ shuf words.txt

这会将打乱的行扔到标准输出;因此,您必须将其通过管道传输到如下输出文件

$ shuf words.txt > shuffled_words.txt

一种这样的洗牌运行可能会产生:

breeze
the
linux
an
ubuntu
good
life
于 2018-03-09T02:26:11.367 回答
6

Python 的一个衬里基于scai 的答案,但是 a) 采用标准输入,b) 使结果可与种子重复,c) 只挑选出所有行中的 200 行。

$ cat file | python -c "import random, sys; 
  random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \
  > 200lines.txt
于 2013-07-22T05:30:18.510 回答
5

我们有一个包来完成这项工作:

sudo apt-get install randomize-lines

例子:

创建一个有序的数字列表,并将其保存到 1000.txt:

seq 1000 > 1000.txt

洗牌,只需使用

rl 1000.txt
于 2016-09-11T14:22:46.017 回答
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()

在 Mac OSX上sort -R并且shuf不可用,因此您可以在 bash_profile 中将其别名为:

alias shuf='python rand.py'
于 2013-07-11T17:48:43.060 回答
3

如果像我一样,您来这里是为了寻找shufmacOS 的替代品,然后使用randomize-lines.

安装randomize-lines(自制)包,它有一个rlshuf.

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
于 2017-02-05T19:21:05.983 回答
2

如果你安装了 Scala,这里有一个单行来打乱输入:

ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
于 2014-06-20T12:37:43.047 回答
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
}
于 2015-01-22T03:34:21.033 回答
0

在 windows 你可以试试这个批处理文件来帮助你打乱你的 data.txt,批处理代码的用法是

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

发出此命令后,maclist_temp.txt 将包含随机的行列表。

希望这可以帮助。

于 2014-04-27T21:20:07.810 回答
0

暂未提及:

  1. 实用unsort程序。语法(有点面向播放列表):

    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
    
于 2017-04-17T16:52:13.733 回答
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];
  }
}
于 2017-12-01T13:33:08.553 回答