How can I shuffle the lines of a text file on the Unix command line or in a shell script?
我想随机移动文本文件的行并创建一个新文件。文件可能有几千行。
我怎样才能用
您可以使用
正如jleedev所指出的:
[编者按:
PerlOne班轮将是Maxim解决方案的简单版本
1 | perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile |
这个答案通过以下方式补充了许多现有的答案:
现有答案打包成灵活的shell函数:
- 函数不仅接受
stdin 输入,还接受文件名参数。 - 这些函数采取额外的步骤,以通常的方式处理
SIGPIPE (安静地终止,退出代码为141 ),而不是大声中断。当将功能输出管道输送到早期关闭的管道时,这一点很重要,例如,当管道输送到head 时。
- 函数不仅接受
进行了性能比较。
- 基于
awk 、sort 和cut 的posix兼容函数,改编自op自己的答案:
1 2 | shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}'"$@" | sort -k1,1n | cut -d ' ' -f2-; } |
- 基于Perl的函数-改编自Moonyoung Kang的答案:
1 | shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);'"$@"; } |
号
- 基于python的函数,改编自scai的答案:
1 2 3 4 5 | 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)) '"$@"; } |
- 基于Ruby的函数,改编自Hoffmanc的答案:
1 2 | 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上获得的。虽然时间会随所用操作系统、机器规格、使用的
输入文件是用
- 埃多克斯1〔12〕
- 埃多克斯1〔13〕
- 红宝石2.0.0
- 埃多克斯1〔14〕
- Perl 5.18.2版
- 埃多克斯1〔15〕
- Python
- 带python 2.7.6的
1.342s ;2.407s !)使用python 3.4.2
- 带python 2.7.6的
awk +sort +cut 。3.003s 与bsdawk ;2.388s 与gnu EDOCX1(4.1.1);1.811s 与mawk (1.3.4);
为了进一步比较,未打包为上述功能的解决方案:
sort -R (如果有重复的输入行,则不是真正的无序排列)10.661s —分配更多的内存似乎没有什么区别。
- 斯卡拉
- 江户十一〔29〕号
bash 环+sort 环- 埃多克斯1〔32〕
结论:
- 如果可以的话,使用
shuf ,这是目前为止最快的。 - Ruby做得很好,后面是Perl。
- Python明显比Ruby和Perl慢,而且,与Python版本相比,2.7.6比3.4.1快得多。
- 使用符合POSIX的
awk +sort +cut 组合作为最后手段;您使用的awk 实现事项(mawk 比gnuawk 快,bsdawk 慢)。 - 远离
sort -R 、bash 环和scala。
我使用一个小Perl脚本,我称之为"unsort":
1 2 3 4 | #!/usr/bin/perl use List::Util 'shuffle'; @list = <STDIN>; print shuffle(@list); |
。
我还有一个空分隔的版本,叫做"unsort0"…方便与find-print0等一起使用。
附言:我也投了"嘘"的票,我不知道这些天在科雷Utils那里……如果您的系统没有"shuf",上面的内容可能仍然有用。
这是第一次尝试,它在编码器上很容易,但在CPU上很难,它为每一行预先准备一个随机数,对它们进行排序,然后从每一行中去掉随机数。实际上,这些行是随机排序的:
1 | cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled |
这是一个awk脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
号
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | $ cat file 1 2 3 4 5 6 7 8 9 10 $ ./shell.sh 7 5 10 9 6 8 2 1 3 4 |
针对python的一行程序:
1 | 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的
简单的基于awk的函数将完成以下工作:
1 2 3 4 | shuffle() { awk 'BEGIN{srand();} {printf"%06d %s ", rand()*1000000, $0;}' | sort -n | cut -c8- } |
。
用途:
1 | any_command | shuffle |
。
这几乎可以在任何UNIX上工作。在Linux、Solaris和HP-UX上测试。
更新:
注意,前导零(
红宝石FTW:
1 | ls | ruby -e 'puts STDIN.readlines.shuffle' |
号
一个基于scai答案的python行程序,但是a)接受stdin,b)使用seed使结果可重复,c)只选择200行。
1 2 3 | $ cat file | python -c"import random, sys; random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \ > 200lines.txt |
号
一种简单直观的方法是使用
例子:
假设
1 2 3 4 5 6 7 | the an linux ubuntu life good breeze |
号
要洗牌,请执行以下操作:
1 | $ shuf words.txt |
号
这会将无序排列的行抛出到标准输出;因此,您必须将其传输到输出文件,如:
1 | $ shuf words.txt > shuffled_words.txt |
号
这样的洗牌可以产生:
1 2 3 4 5 6 7 | breeze the linux an ubuntu good life |
号
我们有一套方案来完成这项工作:
1 | sudo apt-get install randomize-lines |
例子:
创建编号的有序列表,并将其保存到1000.txt:
1 | seq 1000 > 1000.txt |
。
要洗牌,只需使用
1 | rl 1000.txt |
。
如果像我一样,你来这里是为了寻找替代MacOS的
安装
埃多克斯1〔48〕
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
这是我在主文件夹中保存为rand.py的python脚本:
1 2 3 4 5 6 7 8 9 10 11 12 | #!/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
1 | alias shuf='python rand.py' |
。
如果安装了scala,这里有一个单行程序来处理输入:
1 | ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)' |
号
此bash函数具有最小的依赖关系(仅限sort和bash):
1 2 3 4 5 6 7 8 | shuf() { while read -r x;do echo $RANDOM$'\x1f'$x done | sort | while IFS=$'\x1f' read -r x y;do echo $y done } |
号
至今未提及:
1 2 3 4 | unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic] [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] [--linefeed] [file ...] |
。
1 | seq 10 | msort -jq -b -l -n 1 -c r |
另一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/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 =" "; srand(); } { lines[ rand()] = $0; } END { for( k in lines ){ print lines[k]; } } |
。
在Windows中,您可以尝试此批处理文件以帮助您洗牌data.txt,批处理代码的用法是
1 | C:\> type list.txt | shuffle.bat > maclist_temp.txt |
号
发出此命令后,maclist_temp.txt将包含行的随机列表。
希望这有帮助。