在Bash脚本中,我想从输入文件中随机挑选N行并输出到另一个文件。
如何做到这一点呢?
在Bash脚本中,我想从输入文件中随机挑选N行并输出到另一个文件。
如何做到这一点呢?
当前回答
随机排序文件,并选择前100行:
lines=100
input_file=/usr/share/dict/words
# This is the basic selection method
<$input_file sort -R | head -n $lines
# If the file has duplicates that must never cause duplicate results
<$input_file sort | uniq | sort -R | head -n $lines
# If the file has blank lines that must be filtered, use sed
<$input_file sed $'/^[ \t]*$/d' | sort -R | head -n $lines
当然<$input_file可以用任何管道标准输入替换。This (sort -R and $'…\t…'来让sed匹配制表符)适用于GNU/Linux和BSD/macOS。
其他回答
根据对洗牌答案的评论,他在一分钟内洗牌了7800000000行。
接受挑战……
编辑:我打破了自己的记录
Powershuf只用了0.047秒
$ time ./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null
./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null 0.02s user 0.01s system 80% cpu 0.047 total
它这么快的原因是,我没有读取整个文件,只是移动文件指针10次,打印指针后面的行。
Gitlab回购
旧的尝试
首先,我需要一个78.000.000.000行的文件:
seq 1 78 | xargs -n 1 -P 16 -I% seq 1 1000 | xargs -n 1 -P 16 -I% echo "" > lines_78000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000.txt > lines_78000000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000000.txt > lines_78000000000.txt
这给了我一个有780亿换行符的文件;-)
现在是洗牌部分:
$ time shuf -n 10 lines_78000000000.txt
shuf -n 10 lines_78000000000.txt 2171.20s user 22.17s system 99% cpu 36:35.80 total
瓶颈是CPU和不使用多线程,它固定了1个核在100%,其他15个没有使用。
Python是我经常使用的,所以我将使用它来提高速度:
#!/bin/python3
import random
f = open("lines_78000000000.txt", "rt")
count = 0
while 1:
buffer = f.read(65536)
if not buffer: break
count += buffer.count('\n')
for i in range(10):
f.readline(random.randint(1, count))
这只花了我不到一分钟:
$ time ./shuf.py
./shuf.py 42.57s user 16.19s system 98% cpu 59.752 total
我用的是联想X1 extreme 2代i9和三星NVMe,它们给了我很高的读写速度。
我知道它可以变得更快,但我会留一些空间给其他人尝试。
Line counter来源:Luther Blissett
下面的“c”是要从输入中选择的行数。根据需要修改:
#!/bin/sh
gawk '
BEGIN { srand(); c = 5 }
c/NR >= rand() { lines[x++ % c] = $0 }
END { for (i in lines) print lines[i] }
' "$@"
只是为了完整起见,因为它可以从Arch的社区repos中获得:还有一个名为shuffle的工具,但它没有任何命令行开关来限制行数,并在手册页中警告:“由于shuffle将输入读取到内存中,它可能会在非常大的文件上失败。”
随机排序文件,并选择前100行:
lines=100
input_file=/usr/share/dict/words
# This is the basic selection method
<$input_file sort -R | head -n $lines
# If the file has duplicates that must never cause duplicate results
<$input_file sort | uniq | sort -R | head -n $lines
# If the file has blank lines that must be filtered, use sed
<$input_file sed $'/^[ \t]*$/d' | sort -R | head -n $lines
当然<$input_file可以用任何管道标准输入替换。This (sort -R and $'…\t…'来让sed匹配制表符)适用于GNU/Linux和BSD/macOS。
使用shuf和-n选项,如下所示,得到N个随机行:
shuf -n N input > output