如何在Unix命令行或shell脚本中打乱文本文件的行?
我想随机随机洗牌的行,并创build一个新的文件。 该文件可能有几千行。
我怎么能用猫,awk,cut等来做到这一点?
你可以使用shuf
。 至less在某些系统上(似乎不在POSIX中)。
jleedev指出: sort -R
也可能是一个选项。 至less在一些系统上; 好吧,你得到的照片。 有人指出 sort -R
并不真正洗牌,而是根据它们的散列值对项目进行sorting。
[编者按: sort -R
几乎洗牌,除了重复的行/sorting键总是彼此相邻 。 换句话说:只有唯一的input行/键才是真正的洗牌。 虽然输出顺序是由散列值确定的,但随机性来自于select随机散列函数 – 请参阅手册 。
Perl单线程将是Maxim解决scheme的简单版本
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
这个答案在以下几个方面补充了现有的很多答案:
-
现有的答案被打包成灵活的shell函数 :
- 这些函数不仅要使用
stdin
input,还要使用文件名参数 - 这些函数采取了额外的步骤来以通常的方式处理
SIGPIPE
(安静地终止,退出代码为141
),而不是打破喧闹。 将function输出pipe道配pipe到提前closures的pipe道时(例如pipe道到达pipe道时),这一点非常重要。
- 这些函数不仅要使用
-
性能比较 。
- 基于
awk
,sort
和cut
的符合POSIX标准的函数,根据OP自己的回答改编:
shuf() { awk 'BEGIN {srand(); OFMT="%.17f"} {print rand(), $0}' "$@" | sort -k1,1n | cut -d ' ' -f2-; }
- 基于Perl的函数 – 改编自Moonyoung Kang的答案 :
shuf() { perl -MList::Util=shuffle -e 'print shuffle(<>);' "$@"; }
- 基于Python的函数,改编自scai的答案 :
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的回答 :
shuf() { ruby -e 'Signal.trap("SIGPIPE", "SYSTEM_DEFAULT"); puts ARGF.readlines.shuffle' "$@"; }
性能比较:
注意:这些数字是在2012年底采用3.2 GHz Intel Core i5的iMac和运行OSX 10.10.3的Fusion Drive获得的。 虽然计时会随着使用的操作系统,机器规格, awk
实现而变化 (例如OSX上使用的BSD awk
版本通常比GNU awk
尤其是mawk
), 但这应该提供相对性能的一般意义 。
input文件是使用seq -f 'line %.0f' 1000000
生成的1百万行文件 。
时间按升序排列(最快的):
-
shuf
-
0.090s
-
- Ruby 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
与BSDawk
; GNUawk
2.388s
(4.1.1); (1.3.4);
-
为了进一步比较,这些解决scheme未被封装为上述function:
-
sort -R
(如果有重复的input行,则不是真正的随机播放)-
10.661s
– 分配更多的内存似乎没有什么差别
-
- 斯卡拉
-
24.229s
-
-
bash
循环+sort
-
32.593s
-
结论 :
- 如果可以的
shuf
,使用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”:
#!/usr/bin/perl use List::Util 'shuffle'; @list = <STDIN>; print shuffle(@list);
我也有一个NULL分隔的版本,称为“unsort0”…方便使用find -print0等。
PS:我也注意到了'shuf',我不知道coreutils这些日子里是否有这个function……如果你的系统没有'shuf',上面的内容可能仍然有用。
这是第一次在编码器上很容易,但是在CPU上很难,它在每一行上都有一个随机数,对它们进行sorting,然后从每一行中去除随机数。 实际上,这些行是随机sorting的:
cat myfile | awk 'BEGIN{srand();}{print rand()"\t"$0}' | sort -k1 -n | cut -f2- > myfile.shuffled
这里是一个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
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
但看到这个post的python的random.shuffle()
的缺点。 很多(超过2080年)的元素都不能很好地工作。
简单的基于awk的函数将完成这项工作:
shuffle() { awk 'BEGIN{srand();} {printf "%06d %s\n", rand()*1000000, $0;}' | sort -n | cut -c8- }
用法:
any_command | shuffle
这应该适用于几乎所有的UNIX。 在Linux,Solaris和HP-UX上进行testing。
更新:
请注意,前导零( %06d
)和rand()
乘法使其在sort
不理解数字的系统上也能正常工作。 它可以通过字典顺序排列(又称普通string比较)。
一个基于scai的答案 Python的class轮,但a)采取标准input,b)使结果重复与种子,c)挑出所有行中只有200。
$ cat file | python -c "import random, sys; random.seed(100); print ''.join(random.sample(sys.stdin.readlines(), 200))," \ > 200lines.txt
Ruby FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
这是一个Python脚本,我保存为我的家庭文件夹中的rand.py:
#!/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
不可用,因此您可以在shuf
其别名为:
alias shuf='python rand.py'
我们有一个包可以做这个工作:
sudo apt-get install randomize-lines
例:
创build一个有序的数字列表,并将其保存到1000.txt:
seq 1000 > 1000.txt
洗牌,简单地使用
rl 1000.txt
如果像我这样来到这里寻找一个替代macOS的shuf
,然后使用randomize-lines
。
安装randomize-lines
(homebrew)软件包,它有一个与shuf
类似的function的rl
命令。
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
如果你已经安装了Scala,下面是一个简单的input框:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
这个bash函数有最小的依赖(只有sorting和bash):
shuf() { while read -rx;do echo $RANDOM$'\x1f'$x done | sort | while IFS=$'\x1f' read -rxy;do echo $y done }
在Windows中你可以尝试这个batch file来帮助你洗牌你的data.txt,批处理代码的用法是
C:\> type list.txt | shuffle.bat > maclist_temp.txt
发出这个命令后,maclist_temp.txt将包含一个随机的行列表。
希望这可以帮助。
尚未提及:
-
unsort
util。 语法(有点播放列表):unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic] [--identity] [--filenames[=profile]] [--separator sep] [--concatenate] [--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null] [--linefeed] [file ...]
-
msort
可以按msort
洗牌,但通常是过度的:seq 10 | msort -jq -b -l -n 1 -cr