在Unix命令行中从文件读取随机行的简单方法是什么?


Answers:


383

您可以使用shuf

shuf -n 1 $FILE

还有一个名为的实用程序rl。在Debian中,它randomize-lines确实可以满足您的需求,尽管并非在所有发行版中都可用。实际上,它在其主页上建议使用shuf代替(我相信它在创建时就不存在)。 shuf是GNU coreutils的一部分,rl不是。

rl -c 1 $FILE

2
感谢您的shuf提示,它是Fedora中内置的。

5
而且,如果要处理相当大的文件(80kk行),sort -R则肯定会花很多时间,而它们的shuf -n作用却是瞬间的。
鲁本斯

23
您可以通过coreutils从Homebrew 安装在OS X上获取shuf 。可以被称为gshuf替代shuf
艾丽莎·罗斯

2
同样,您可以randomize-lines在OS X上使用以下版本:brew install randomize-lines; rl -c 1 $FILE
Jamie 2014年

4
请注意,它shufGNU Coreutils的一部分,因此不一定在* BSD系统(或Mac?)上可用(默认情况下)。@ Tracker1的perl一线以下更轻便(根据我的测试,速度稍快)。
亚当·卡兹

74

另一种选择:

head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1

28
$ {RANDOM}仅生成小于32768的数字,因此请勿将其用于大文件(例如英语词典)。
拉尔夫(Ralf)2012年

3
由于取模运算,这不会给您每行精确的相同概率。如果文件长度<< 32768,则几乎无关紧要(如果将其除以该数字,则完全没有关系),但也许值得注意。
照应2014年

10
您可以使用将其扩展为30位随机数(${RANDOM} << 15) + ${RANDOM}。这样可以大大减少偏差,并使它可以用于包含多达10亿行的文件。
nneonneo

@nneonneo:非常酷的技巧,尽管根据此链接,它应该是对$ {RANDOM}进行“或”运算,而不是对stackoverflow.com/a/19602060/293064进行加号运算
周杰伦泰勒

+并且|相同,因为${RANDOM}根据定义为0..32767。
nneonneo 2015年

70
sort --random-sort $FILE | head -n 1

(尽管我更喜欢上面的shuf方法-我什至不知道它的存在,而且我永远不会自己找到该工具)


10
+1我喜欢,但您可能需要最新版本sort,但在我的任何系统(CentOS 5.5,Mac OS 10.7.2)上均不起作用。此外,对猫的无用使用可能会减少为sort --random-sort < $FILE | head -n 1
Steve Kehlet12年

sort -R <<< $'1\n1\n2' | head -1可能返回1和2,因为将sort -R重复的行排序在一起。这同样适用于sort -Ru,因为它会删除重复的行。
Lri 2012年

5
这相对较慢,因为整个文件在传递到sort之前需要经过重新排序headshuf而是从文件中选择随机行,这对我来说要快得多。
Bengt 2012年

1
@SteveKehlet sort --random-sort $FILE | head最好,因为它允许它直接访问文件,从而有可能实现高效的并行排序
WaelJ 2014年

5
--random-sort-R选项特定于GNU排序(所以他们不会与BSD或Mac OS工作sort)。GNU sort在2005年学习了这些标志,因此您需要GNU coreutils 6.0或更高版本(例如CentOS 6)。
RJHunter 2015年

31

这很简单。

cat file.txt | shuf -n 1

当然,这只比“ shuf -n 1 file.txt”慢一点。


2
最佳答案。我不知道这个命令。注意,-n 1指定了1行,您可以将其更改为1行以上shuf。我只是通过管道传输ps auxgrep随机杀死部分匹配名称的进程。
sudo

18

perlfaq5:如何从文件中选择随机行?这是骆驼书中的储层采样算法:

perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file

与读取整个文件相比,这在空间方面具有显着优势。您可以在Donald E. Knuth撰写的The Art of Computer Programming,第2卷,第3.4.2节中找到这种方法的证明。


1
仅出于包含目的(以防引用站点崩溃),这是Tracker1指向的代码:“ cat filename | perl -e'while(<>){push(@ _,$​​ _);} print @ _ [rand()* @ _];';“
阿尼尔万

3
这对猫是无用的。这是对perlfaq5中的代码的略微修改(并由Camel的书提供):perl -e'srand; rand($。)<1 &&($ line = $ _)而<>; 打印$ line;' 文件名
Muskrat先生

err ...链接的站点,即
Nathan Fellman

我刚刚以此代码为基准,对该代码的N行版本进行了基准测试shuf。Perl代码稍快一些(通过用户时间快了8%,通过系统时间快了24%),尽管我发现Perl代码“似乎”不太随机(我用它写了一个点唱机)。
亚当·卡兹

2
值得深思的是: shuf将整个输入文件存储在内存中,这是一个可怕的想法,而此代码仅存储一行,因此此代码的限制是INT_MAX的行数(2 ^ 31或2 ^ 63,具体取决于您的拱形),并假设其已选择的任何潜在线都适合存储。
亚当·卡兹

11

使用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}

1
随机可以为0,sed第一行需要1。sed -n 0p返回错误。
asalamon74

嗯-“ tmp.txt” $ 1和NUM $ 2怎么样?
blabla999

但即使是值得一提的错误,它也不需要perl或python,并且可以达到您所需要的效率(将文件读取两次,但不读取到内存中-因此即使是大型文件也可以使用)。
blabla999

@ asalamon74:谢谢@ blabla999:如果我们用它做一个函数,可以$ 1,但是为什么不计算NUM呢?
Paolo Tedesco,

将sed行更改为:head-$ {X} $ {FILE} | 尾巴-1应该做到这一点
JeffK

4

单bash行:

sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt

轻微问题:文件名重复。


2
轻微的问题。在/ usr / share / dict / words上执行此操作倾向于使用以“ A”开头的单词。玩它,我大约有90%的“ A”字到10%的“ B”字。尚无以数字开头的数字,它是文件的开头。
bibby 2010年

wc -l < test.txt避免不得不用管道cut
fedorqui'SO停止伤害'2015年

3

这是一个可以完成此工作的简单Python脚本:

import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])

用法:

python randline.py file_to_get_random_line_from

1
这不太有效。它在一行之后停止。为了使它起作用,我做到了:import random, sys lines = open(sys.argv[1]).readlines() 对于i在range(len(lines))中:rand = random.randint(0,len(lines)-1)print lines.pop(rand),
Jed Daniels

笨拙的注释系统,格式笨拙。注释格式化不是曾经起作用过吗?
Jed Daniels

randint是包容性的,因此len(lines)可能导致IndexError。您可以使用print(random.choice(list(open(sys.argv[1]))))。还有一种内存有效的储层采样算法
jfs 2014年

2
相当饥饿的空间;考虑一个3TB的文件。
迈克尔·坎贝尔

@MichaelCampbell:我上面提到的储层采样算法可能适用于3TB文件(如果行大小受到限制)。
jfs

2

使用' awk '的另一种方式

awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name

2
它使用awk和bash($RANDOMbashism)。这是一种纯awk(mawk)方法,其使用与@ Tracker1引用的上述perlfaq5代码相同的逻辑:(awk 'rand() * NR < 1 { line = $0 } END { print line }' file.name哇,它甚至比perl代码还要!)
Adam Katz

该代码必须读取文件(wc)以获得行数,然后必须再次读取文件(的一部分)(awk)以获取给定随机行号的内容。I / O比获得随机数要昂贵得多。我的代码仅读取一次文件。awk的问题rand()在于它是基于秒的种子,因此如果连续运行太快,则会获得重复。
亚当·卡兹

1

一个解决方案也可以在MacOSX上运行,并且也可以在Linux(?)上运行:

N=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)- >绘制N随机(数字-r范围内)(1, number_of_line_in_file)jot。流程替换<()将使它看起来像是解释器的文件,因此file1在前面的示例中。

0
#!/bin/bash

IFS=$'\n' wordsArray=($(<$1))

numWords=${#wordsArray[@]}
sizeOfNumWords=${#numWords}

while [ True ]
do
    for ((i=0; i<$sizeOfNumWords; i++))
    do
        let ranNumArray[$i]=$(( ( $RANDOM % 10 )  + 1 ))-1
        ranNumStr="$ranNumStr${ranNumArray[$i]}"
    done
    if [ $ranNumStr -le $numWords ]
    then
        break
    fi
    ranNumStr=""
done

noLeadZeroStr=$((10#$ranNumStr))
echo ${wordsArray[$noLeadZeroStr]}

由于$ RANDOM生成的数字小于/ usr / share / dict / words中的单词数(无论如何在我的Mac上为235886),我只生成6个介于0和9之间的独立随机数并将它们串在一起。然后,确保该数字小于235886。然后删除前导零以索引我存储在数组中的单词。由于每个单词都是自己的一行,因此可以轻松地将其用于任何文件以随机选择一行。

0

这是我发现的,因为我的Mac OS并未使用所有简单的答案。我使用jot命令生成一个数字,因为$ RANDOM变量解决方案在我的测试中似乎不是很随机。测试我的解决方案时,输出中提供的解决方案差异很大。

  RANDOM1=`jot -r 1 1 235886`
   #range of jot ( 1 235886 ) found from earlier wc -w /usr/share/dict/web2
   echo $RANDOM1
   head -n $RANDOM1 /usr/share/dict/web2 | tail -n 1

变量的回声是生成的随机数的可视化。


0

仅使用香草sed和awk,而不使用$ RANDOM,从名称为FILENAME的文件中伪随机地选择单行的一种简单,节省空间且相当快的“单行”如下所示:

sed -n $(awk 'END {srand(); r=rand()*NR; if (r<NR) {sub(/\..*/,"",r); r++;}; print r}' FILENAME)p FILENAME

(即使FILENAME为空,此方法也有效,在这种情况下,不会发出任何行。)

这种方法的一个可能的优点是它只调用rand()一次。

正如@AdamKatz在评论中指出的,另一种可能性是为每一行调用rand():

awk 'rand() * NR < 1 { line = $0 } END { print line }' FILENAME

(可以根据归纳给出正确性的简单证明。)

注意事项 rand()

“在大多数awk实现中,包括gawk,每次运行awk时,rand()都会从相同的起始数字或种子开始生成数字。”

- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html


请参阅我在此答案之前一年发表的评论,它提供了一个更简单的awk解决方案,不需要sed。还要注意我关于awk随机数生成器的警告,该生成器整秒钟播种。
亚当·卡兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.