在shell脚本中,从文件中随机读取一行的简单方法是什么?
shuf
:shuf -n 1 $FILE
还有一个叫做rl
的实用工具。在Debian中,它在randomize-lines
软件包中,正好可以满足你的需求,尽管并不是所有发行版都可用。在其主页上,它实际上推荐使用shuf
(我相信当它被创建时,shuf
还不存在)。shuf
是GNU coreutils的一部分,而rl
则不是。
rl -c 1 $FILE
shuf
的提示,它在 Fedora 中是内置的。 - Chengsort -R
肯定会让人等待很长时间,而 shuf -n
则可以立即执行。 - Rubensrandomize-lines
命令,方法是 brew install randomize-lines; rl -c 1 $FILE
。 - Jamieshuf
是 GNU Coreutils 的一部分,因此在 *BSD 系统(或 Mac?)中不一定会默认提供。@Tracker1 下面的 perl 一行代码更具可移植性(而且根据我的测试,速度略快)。 - Adam Katz另一种选择:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
来扩展到30位随机数。这样可以显著减小偏差,并且适用于包含多达10亿行的文件。 - nneonneo${RANDOM}
的定义范围是0..32767。 - nneonneosort --random-sort $FILE | head -n 1
(我更喜欢上面的随机排序方法 - 我甚至不知道它的存在,我自己也永远找不到那个工具)
sort
,因为它在我的任何系统上都无法工作(CentOS 5.5,Mac OS 10.7.2)。此外,使用 cat 是无用的,可以简化为 sort --random-sort < $FILE | head -n 1
。 - Steve Kehletsort
对整个文件进行洗牌(shuffle),然后再将结果输出给 head
。相反,shuf
会直接选择文件中的随机行,速度更快。 - Bengtsort --random-sort $FILE | head
是最好的选择,因为它允许直接访问文件,可能实现高效并行排序。 - WaelJshuf
将整个文件读入内存之前,什么都没有被写入。即使文件不适合内存,sort
也可以工作。 - jfs这很简单。
cat file.txt | shuf -n 1
虽然比起单独使用“shuf -n 1 file.txt”命令要慢一点
-n 1
指定了1行,您可以将其更改为多于1行。 shuf
也可以用于其他事情; 我只是将ps aux
和grep
与它一起使用,以随机杀死部分匹配名称的进程。 - sudoperlfaq5:如何从文件中选择一行随机? 以下是来自“Camel Book”的水塘取样算法:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
相比于将整个文件读入内存,这种方法在空间上具有显著的优势。您可以在《计算机程序设计艺术》第2卷第3.4.2节中找到此方法的证明,作者是Donald E. Knuth。
shuf
进行了比较。Perl代码速度略快(用户时间快8%,系统时间快24%),尽管从个人经验来看,我发现Perl代码“似乎”不太随机(我使用它编写了一个点唱机)。 - Adam Katzshuf
将整个输入文件存储在内存中,这是一个可怕的想法,而这段代码只存储一行,因此该代码的限制是INT_MAX(2^31或2^63,具体取决于您的架构)的行数,假设其选择的任何潜在行都适合内存。 - Adam Katzawk 'BEGIN{srand()}{rand()*NR<1&&l=$0}END{print l}' file
或者some_input | awk 'BEGIN{srand()}{rand()*NR<1&&l=$0}END{print l}'
- keithpjolley使用 Bash 脚本:
#!/bin/bash
# replace with file to read
FILE=tmp.txt
# count number of lines
NUM=$(wc - l < ${FILE})
# generate random number in range 0-NUM
let X=${RANDOM} % ${NUM} + 1
# extract X-th line
sed -n ${X}p ${FILE}
$FILE
。这里的花括号是多余的。我建议使用小写或混合大小写的变量名,以避免与shell或环境变量可能发生的名称冲突。 - Dennis Williamsonwc -l
不应该有空格。 - Lri单行Bash命令:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
小问题:文件名重复。
wc -l < test.txt
避免了需要使用管道到 cut
的情况。 - fedorquiimport random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
使用方法:
python randline.py file_to_get_random_line_from
import random, sys lines = open(sys.argv[1]).readlines()
len(lines)
可能会导致IndexError。您可以使用print(random.choice(list(open(sys.argv[1]))))
。还有一种内存高效的蓄水池抽样算法。 - jfs-l
将输入的行分配给列表l
。 py
自动导入stdlib模块,因此您可以执行cat $FILE | py -l "random.choice(l)"
。试试:python -m this | py -l "random.choice(l)"
... 呃实际上只需py this | py -l "random.choice(l)"
;) - floer32使用'awk'的另一种方法
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
$RANDOM
是 bashism)。以下是一个纯 awk(mawk)方法,使用与 @Tracker1 引用的 perlfaq5 代码相同的逻辑:awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name
(哇,它甚至比 perl 代码还要短!) - Adam Katzwc
)以获取行数,然后必须再次读取(部分)文件(awk
)以获取给定随机行号的内容。I/O比获取随机数要昂贵得多。我的代码仅读取一次文件。awk的rand()
问题在于它基于秒种子生成,因此如果您连续运行它太快,就会得到重复的结果。 - Adam KatzN=5
awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
其中:
N
是您想要的随机行数。
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
--> 保存在file1
中写入的行号,然后打印出file2
相应的行。
jot -r $N 1 $(wc -l < $file)
--> 使用jot
在范围(1,文件总行数)
内随机绘制N
个数字(-r
)。进程替换<()
会让它看起来像是解释器的一个文件,所以前面例子中的file1
。