我想随机打乱文本文件的行并创建一个新文件。该文件可能有数千行。
我怎么能用cat
, awk
,cut
等来做到这一点?
Perl one-liner 将是 Maxim 解决方案的简单版本
perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' < myfile
该答案通过以下方式补充了许多现有的答案:
现有答案被打包成灵活的shell 函数:
stdin
输入,还接受文件名参数SIGPIPE
通常的方式处理(使用退出代码安静终止141
),而不是吵闹。这在将函数输出通过管道传输到提前关闭的管道时很重要,例如当管道传输到head
.进行了性能比较。
awk
,sort
改编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' "$@"; }
性能对比:
注意:这些数字是在 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
0.289s
0.589s
1.342s
使用 Python 2.7.6;2.407s
(!) 使用 Python 3.4.2awk
+ sort
+cut
3.003s
与 BSD awk
; 2.388s
使用 GNU awk
(4.1.1);1.811s
与mawk
(1.3.4);为了进一步比较,未打包为上述功能的解决方案:
sort -R
(如果有重复的输入行,则不是真正的随机播放)
10.661s
- 分配更多内存似乎没有什么区别24.229s
bash
循环 +sort
32.593s
结论:
shuf
如果可以,请使用- 这是迄今为止最快的。awk
++sort
组合cut
作为最后的手段;您使用哪种awk
实现很重要(mawk
比 GNU 快awk
,BSDawk
最慢)。sort -R
、bash
循环和 Scala。Windows版本的Python解决方案(Python 代码是相同的,除了引用的变化和信号相关语句的删除,Windows 不支持这些语句):
$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
cmdlet 本地随机播放(尽管性能可能是个问题);例如:
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))" %*
我使用一个很小的 perl 脚本,我称之为“unsort”:
#!/usr/bin/perl
use List::Util 'shuffle';
@list = <STDIN>;
print shuffle(@list);
我还有一个以 NULL 分隔的版本,称为“unsort0”……方便与 find -print0 等一起使用。
PS:也投了“shuf”的票,我不知道这些天在 coreutils 中存在……如果您的系统没有“shuf”,上述内容可能仍然有用。
这是第一次尝试,它在编码器上很容易,但在 CPU 上却很难,它为每行添加一个随机数,对它们进行排序,然后从每行中去除随机数。实际上,这些行是随机排序的:
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
但请参阅这篇文章了解 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 上测试。
更新:
请注意,前导零 ( %06d
) 和rand()
乘法使其在sort
不理解数字的系统上也能正常工作。它可以通过字典顺序(又名普通字符串比较)进行排序。
红宝石 FTW:
ls | ruby -e 'puts STDIN.readlines.shuffle'
一种简单直观的方法是使用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
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
我们有一个包来完成这项工作:
sudo apt-get install randomize-lines
例子:
创建一个有序的数字列表,并将其保存到 1000.txt:
seq 1000 > 1000.txt
洗牌,只需使用
rl 1000.txt
这是我在主文件夹中保存为 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'
如果像我一样,您来这里是为了寻找shuf
macOS 的替代品,然后使用randomize-lines
.
安装randomize-lines
(自制)包,它有一个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
如果你安装了 Scala,这里有一个单行来打乱输入:
ls -1 | scala -e 'for (l <- util.Random.shuffle(io.Source.stdin.getLines.toList)) println(l)'
此 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
}
在 windows 你可以试试这个批处理文件来帮助你打乱你的 data.txt,批处理代码的用法是
C:\> type list.txt | shuffle.bat > maclist_temp.txt
发出此命令后,maclist_temp.txt 将包含随机的行列表。
希望这可以帮助。
暂未提及:
实用unsort
程序。语法(有点面向播放列表):
unsort [-hvrpncmMsz0l] [--help] [--version] [--random] [--heuristic]
[--identity] [--filenames[=profile]] [--separator sep] [--concatenate]
[--merge] [--merge-random] [--seed integer] [--zero-terminated] [--null]
[--linefeed] [file ...]
msort
可以逐行洗牌,但通常是矫枉过正:
seq 10 | msort -jq -b -l -n 1 -c r
另一种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];
}
}