Bash 使用sed的随机线

Bash 使用sed的随机线,bash,sed,Bash,Sed,我想用sed随机选择一行。我知道shuf-n和sort-R | head-n可以完成这项工作,但是对于shuf您必须安装coreutils,对于排序解决方案来说,它在大数据上不是最优的: 以下是我测试的内容: echo "$var" | shuf -n1 这提供了最佳解决方案,但我担心可移植性 这就是为什么我想用sed试试它 `var="Hi i am a student learning scripts"` output: i am a student output: hi 它一定

我想用
sed
随机选择一行。我知道
shuf-n
sort-R | head-n
可以完成这项工作,但是对于
shuf
您必须安装
coreutils
,对于
排序解决方案来说,它在大数据上不是最优的:

以下是我测试的内容:

echo "$var" | shuf -n1
这提供了最佳解决方案,但我担心可移植性 这就是为什么我想用
sed
试试它

`var="Hi
 i am a student
 learning scripts"`

output:
i am a student

output:
hi
它一定是随机的

var="Hi
i am a student
learning scripts"

mapfile -t array <<< "$var"      # create array from $var

echo "${array[$RANDOM % (${#array}+1)]}"
echo "${array[$RANDOM % (${#array}+1)]}"


请参阅:
帮助映射文件

它在很大程度上取决于您希望伪随机概率分布的外观。(不要尝试随机,满足于伪随机。如果你成功地生成了一个真正的随机值,就去领取诺贝尔奖。)如果你只是想要一个均匀分布(例如,每行被选中的概率相等),那么你需要事先知道文件中有多少行。获得该分发版并不像允许选择文件中较早的行那样容易,因为这很容易,所以我们将这样做。假设行数小于32769,您只需执行以下操作:

N=$(wc -l < input-file)
sed -n -e $((RANDOM % N + 1))p input-file
$ echo -e $var >varfile
$ i=;while read a; do let i++;done<varfile; let RANDOM=i*i*i

$ let l=$RANDOM%$i+1 ;sed -En "$l p" varfile
N=$(wc-l
--编辑--

在考虑了一会儿之后,我意识到你不需要知道行数,所以你不需要读取数据两次。我没有做过严格的分析,但我相信下面给出了一个统一的分布:

awk 'BEGIN{srand()} rand() < 1/NR { out=$0 } END { print out }' input-file
awk'BEGIN{srand()}rand()<1/NR{out=$0}END{print out}输入文件
--编辑-- Ed Morton在评论中建议我们只能调用rand()一次。这似乎应该行得通,但似乎行不通。好奇的:

$ time for i in $(seq 400); do awk -v seed=$(( $(date +%s) + i)) 'BEGIN{srand(seed); r=rand()} r < 1/NR { out=$0 } END { print out}'  input; done | awk '{a[$0]++} END { for (i in a) print i, a[i]}' | sort
1 205
2 64
3 37
4 21
5 9
6 9
7 9
8 46

real    0m1.862s
user    0m0.689s
sys     0m0.907s
$ time for i in $(seq 400); do awk -v seed=$(( $(date +%s) + i)) 'BEGIN{srand(seed)} rand() < 1/NR { out=$0 } END { print out}'  input; done | awk '{a[$0]++} END { for (i in a) print i, a[i]}' | sort
1 55
2 60
3 37
4 50
5 57
6 45
7 50
8 46

real    0m1.924s
user    0m0.710s
sys     0m0.932s
$time for i,单位为$(seq 400);do awk-v seed=$($(date+%s)+i))'开始{srand(seed);r=rand()}r<1/NR{out=$0}结束{print out}'输入;完成| awk'{a[$0]+}END{for(a中的i)print i,a[i]}排序
1 205
2 64
3 37
4 21
5 9
6 9
7 9
8 46
实0m1.862s
用户0m0.689s
系统0m0.907s
以美元表示的i的时间(seq 400);do awk-v seed=$($(date+%s)+i))'BEGIN{srand(seed)}rand()<1/NR{out=$0}END{print out}输入;完成| awk'{a[$0]+}END{for(a中的i)print i,a[i]}排序
1 55
2 60
3 37
4 50
5 57
6 45
7 50
8 46
真正的0m1.924s
用户0.710s
系统0m0.932s

这似乎是大型输入文件的最佳解决方案:

awk -v seed="$RANDOM" -v max="$(wc -l < file)" 'BEGIN{srand(seed); n=int(rand()*max)+1} NR==n{print; exit}' file

因此,我们预先调用
wc
rand()
一次的解决方案比按预期为每一行调用
rand()
要快。

在bash shell上,首先将种子初始化为#line cube或您的选择

$ i=;while read a; do let i++;done<<<$var; let RANDOM=i*i*i

$ let l=$RANDOM%$i+1 ;echo -e $var |sed -En "$l p"
使用GNU
sed
bash
;no
wc
awk
将最后一个内部循环放入((c=0;c):

f=input-file
sed -n $((RANDOM%($(sed = $f | sed '2~2d' | sed -n '$p')) + 1))p $f

注意:
$(…)
中的三个
sed
是伪造
wc-l
的低效方法。也许有更好的方法——当然,只使用
sed

使用
shuf

$ echo "$var" | shuf -n 1
输出:

Hi

欢迎来到SO。Stack Overflow是一个面向专业和热情程序员的问答页面。请在问题中添加您自己的代码。您至少需要展示您自己为解决此问题所做的研究。谢谢,是的,没问题,我会编辑。已知的行数是多少?使用bash和sed执行此操作将非常简单效率更低,安装coreutils有什么问题?为什么需要专门使用sed?几乎每个系统上都有很多工具,可能更适合使用。我喜欢awk解决方案,我也同意,它似乎应该可以工作,但你不能调用
rand()
在BEGIN部分中使用一次变量,而不是每行输入调用一次变量?因为srand()默认情况下使用当前秒数(从历元值算起)进行种子设定,如果在1秒内运行两次,它将生成相同的输出-如果您愿意,可以通过
awk-v seed=“$RANDOM”BEGIN{srand(seed)更改该值“
rand()
的后续调用将产生新的值…只有重新生成awk时才是一样的。但是你是对的!我们只需要调用一次!这将大大加快速度。这就是我所说的重新生成awk的情况。你不可能在1秒内调用两次,但如果调用了,你会得到相同的输出。”(除非你运气好,并且在大纪元改变后过了一秒)。就个人而言,我通常不想处理这个问题,但由于OP似乎非常关注随机性,我想我建议
seed=“$RANDOM”
如果有问题的话。在BEGIN中只尝试调用rand一次,输出严重偏向文件的开头。不知道为什么…@EdMorton我没有。现在运行脚本计时。@Williampersell-您介意检查上面的内容并尝试计时,看看是否得到类似的结果吗?在我的笔记本电脑上,运行2次脚本(运行wc-l)实际上更快。每次只运行一次,12.864实时,vs 24.625。但我突然想到,我们可能错过了巨大内存的现代奇迹!对于这类事情,现在可能更容易将整个文件读入内存,然后在最后随机选择一行!
$ echo -e $var >varfile
$ i=;while read a; do let i++;done<varfile; let RANDOM=i*i*i

$ let l=$RANDOM%$i+1 ;sed -En "$l p" varfile
f=input-file
sed -n $((RANDOM%($(sed = $f | sed '2~2d' | sed -n '$p')) + 1))p $f
$ echo "$var" | shuf -n 1
Hi