在Unix命令行中从文件读取随机行的简单方法是什么?
在Unix命令行中从文件读取随机行的简单方法是什么?
Answers:
您可以使用shuf
:
shuf -n 1 $FILE
还有一个名为的实用程序rl
。在Debian中,它randomize-lines
确实可以满足您的需求,尽管并非在所有发行版中都可用。实际上,它在其主页上建议使用shuf
代替(我相信它在创建时就不存在)。 shuf
是GNU coreutils的一部分,rl
不是。
rl -c 1 $FILE
shuf
提示,它是Fedora中内置的。
sort -R
则肯定会花很多时间,而它们的shuf -n
作用却是瞬间的。
coreutils
从Homebrew 安装在OS X上获取shuf 。可以被称为gshuf
替代shuf
。
randomize-lines
在OS X上使用以下版本:brew install randomize-lines; rl -c 1 $FILE
shuf
是GNU Coreutils的一部分,因此不一定在* BSD系统(或Mac?)上可用(默认情况下)。@ Tracker1的perl一线以下更轻便(根据我的测试,速度稍快)。
另一种选择:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
(${RANDOM} << 15) + ${RANDOM}
。这样可以大大减少偏差,并使它可以用于包含多达10亿行的文件。
+
并且|
相同,因为${RANDOM}
根据定义为0..32767。
sort --random-sort $FILE | head -n 1
(尽管我更喜欢上面的shuf方法-我什至不知道它的存在,而且我永远不会自己找到该工具)
sort
,但在我的任何系统(CentOS 5.5,Mac OS 10.7.2)上均不起作用。此外,对猫的无用使用可能会减少为sort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
可能返回1和2,因为将sort -R
重复的行排序在一起。这同样适用于sort -Ru
,因为它会删除重复的行。
sort
之前需要经过重新排序head
。shuf
而是从文件中选择随机行,这对我来说要快得多。
sort --random-sort $FILE | head
最好,因为它允许它直接访问文件,从而有可能实现高效的并行排序
--random-sort
和-R
选项特定于GNU排序(所以他们不会与BSD或Mac OS工作sort
)。GNU sort在2005年学习了这些标志,因此您需要GNU coreutils 6.0或更高版本(例如CentOS 6)。
perlfaq5:如何从文件中选择随机行?这是骆驼书中的储层采样算法:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
与读取整个文件相比,这在空间方面具有显着优势。您可以在Donald E. Knuth撰写的The Art of Computer Programming,第2卷,第3.4.2节中找到这种方法的证明。
shuf
。Perl代码稍快一些(通过用户时间快了8%,通过系统时间快了24%),尽管我发现Perl代码“似乎”不太随机(我用它写了一个点唱机)。
shuf
将整个输入文件存储在内存中,这是一个可怕的想法,而此代码仅存储一行,因此此代码的限制是INT_MAX的行数(2 ^ 31或2 ^ 63,具体取决于您的拱形),并假设其已选择的任何潜在线都适合存储。
使用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}
单bash行:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
轻微问题:文件名重复。
wc -l < test.txt
避免不得不用管道cut
。
这是一个可以完成此工作的简单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
import random, sys lines = open(sys.argv[1]).readlines()
对于i在range(len(lines))中:rand = random.randint(0,len(lines)-1)print lines.pop(rand),
使用' awk '的另一种方式
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
wc
)以获得行数,然后必须再次读取文件(的一部分)(awk
)以获取给定随机行号的内容。I / O比获得随机数要昂贵得多。我的代码仅读取一次文件。awk的问题rand()
在于它是基于秒的种子,因此如果连续运行太快,则会获得重复。
一个解决方案也可以在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
在前面的示例中。#!/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]}
这是我发现的,因为我的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
变量的回声是生成的随机数的可视化。
仅使用香草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