我有一个示例,希望在一组字母中(例如var="abcdefghijklmnopqrstuvwxyz")随机生成一个字母。
那么我该怎么做呢?
var="abcdefghijklmnopqrstuvwxyz"
echo "${var}"
yield: 某个字母
我的目标是从我选择的一组随机字母中自动生成一个。无论它是在数组还是字符串中都没有关系。
var="abcdefghijklmnopqrstuvwxyz"
echo "${var:$(( RANDOM % ${#var} )):1}" # pick a 1 char substring starting at a random position
这个方法的原理是:
${var:START:LEN}
是一种参数扩展,它将$var的子字符串扩展出来${#var}
是一种参数扩展,它将字符串变量var的内容长度扩展出来$(( ))
创建一个算术上下文,在该上下文中,非数字字符串被假定为变量名(因此可以使用RANDOM
代替$RANDOM
)。$RANDOM
,每次评估时,都会扩展为0到32767之间的随机整数。$RANDOM % ${#var}
取该随机整数除以命名为var
的字符串中字符数的余数;因此,它将介于0和(var
的长度-1)之间,并且将“几乎”随机地分割(如果var
的长度不能平均地分成32768,则某些字符将比其他字符略微具有更高的被选中的机会)。因此,${var:$(( RANDOM % ${#var} )) : 1}
每次评估时,都会选择字符串内的一个位置,并在其中扩展为单个字符跨度。
RANDOM
时,故事变得稍微复杂(请参见下面的解释)。最好的方法是使用 shuf
。 shuf
生成给定范围的随机排列,并允许您像 shuf -i 0-25 -n1
那样选择第一个数字,因此您可以使用它。var="abcdefghijklmnopqrstuvwxyz"
echo ${var:$(shuf -i 0-$((${#var}-1)) -n1):1}
% for i in {0..32767}; do echo $((i%5)); done | sort -g | uniq -c
6554 0
6554 1
6554 2
6553 3 < smaller change to hit 3
6553 4 < smaller chance to hit 4
n*RANDOM/32768
。但是,这仅适用于生成实数的随机数生成器。 RANDOM
生成的是一个整数。整数缩放实际上只是将之前的问题混淆了:% for i in {0..32767}; do echo $((5*i/32768)); done | sort -g | uniq -c
6554 0
6554 1
6553 2 < smaller chance to hit 2
6554 3
6553 4 < smaller chance to hit 4
RANDOM
,最好的方法是跳过不需要的值,这可以通过简单的while
循环来实现。var="abcdefghijklmnopqrstuvwxyz"
n=${#var}
idx=32769; while (( idx >= (32768/n)*n )); do idx=$RANDOM; done
char=${var:$idx:1}
注意:你有可能会被while循环永远卡住。
注释:我们不评论RANDOM
背后的随机数生成器有多好。我们所做的只是引用源代码中的注释:
source bash 4.4.18 (
variables.c
)
/* A linear congruential random number generator based on the example one in the ANSI C standard. This one isn't very good, but a more complicated one is overkill. */
您还可以以更简洁的方式使用shuf
。
基本上,您将字符串分成每行一个字符,然后以常规方式使用shuf
:
使用fold
进行拆分
echo "abcdefghijklmnopqrstuvwxyz" | fold -w1 | shuf -n1
grep
进行分割echo "abcdefghijklmnopqrstuvwxyz" | grep -o . | shuf -n1
sh
,但如果你需要sh兼容性,一些仅适用于bash的技巧可能不可用。 - Charles Duffyterminal
标签仅适用于关于终端的问题 - 而这不是 - 而linux
标签适用于特定于Linux的问题,这也不是。 - Charles Duffy