在shell脚本中从文件中读取随机行有什么简单的方法?
下面是一个来自Camel Book的水库采样算法:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
与读取整个文件相比,这在空间上有很大的优势。你可以在Donald E. Knuth的《计算机编程艺术》第2卷第3.4.2节中找到这种方法的证明。
下面是一个简单的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
使用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
小问题:重复的文件名。
你可以使用shuf:
shuf
shuf -n 1 $FILE
还有一个名为rl的实用程序。在Debian中,它在randomize-lines包中,尽管不是在所有发行版中都可用,但它确实可以做你想要的事情。在它的主页上,它实际上推荐使用shuf(我相信它创建时并不存在)。shuf是GNU coreutils的一部分,rl不是。
rl
randomize-lines
rl -c 1 $FILE
另一个选择:
head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
sort --random-sort $FILE | head -n 1
(我更喜欢上面的shiff方法——我甚至不知道它的存在,我自己也永远不会找到这个工具)
另一种使用'awk'的方法
awk NR==$((${RANDOM} % `wc -l < file.name` + 1)) file.name
一个解决方案,也适用于MacOSX,应该也适用于Linux(?):
N=5 awk 'NR==FNR {lineN[$1]; next}(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
地点:
N是你想要的随机行数
N
NR==FNR {lineN[$1]; next}(FNR in lineN) file1 file2
file1
file2
jot -r $N 1 $(wc -l < $file)
(1, number_of_line_in_file)
jot
-r
<()
只使用普通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, rand()每次运行awk时都从相同的起始数字或种子开始生成数字。
——https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html
这很简单。
cat file.txt | shuf -n 1
当然,这只是比“shuf -n 1 file.txt”本身稍微慢一点。
#!/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
变量的回声是为了得到生成的随机数的视觉效果。