我有一个例子,我试图在一组字母中随机生成一个字母(即var ="abcdefghijklmnopqrstuvwxyz").
我怎么会:
var="abcdefghijklmnopqrstuvwxyz"
echo "${var}"
Run Code Online (Sandbox Code Playgroud)
让: some_letter
关键是,我正在尝试从我选择的选择中自动生成随机字母.无论是数组还是字符串都没关系.
var="abcdefghijklmnopqrstuvwxyz"
echo "${var:$(( RANDOM % ${#var} )):1}" # pick a 1 char substring starting at a random position
Run Code Online (Sandbox Code Playgroud)
这是因为:
${var:START:LEN}是一个参数扩展,扩展到一个替代$var${#var} 是一个参数扩展,它扩展为字符串变量var的内容的长度$(( ))创建一个算术上下文,其中假定非数字字符串引用变量名称(因此可以使用RANDOM而不是$RANDOM).$RANDOM,每次评估时,都会扩展为0到32767之间的随机整数. $RANDOM % ${#var}将随机整数除以命名字符串中的字符数的剩余部分var; 因此,它将在0和(长度为var-1)之间,并且几乎是随机分割的(如果长度var不均匀地分成32768,那么一些角色的机会会略高一些)被选中比其他人.因此,${var:$(( RANDOM % ${#var} )) : 1}每次评估时,将在字符串中选择一个位置,并扩展到其中的单字符范围.
For most practical cases, the solution of Charles Duffy is the way forward. However, if your random character picking has to be uniform, then the story becomes slightly more complicated when you want to use RANDOM (see explanation below). The best way forward would be the usage of shuf. shuf generates a random permutation of a given range and allows you to pick the first number like shuf -i 0-25 -n1, so you could use
var="abcdefghijklmnopqrstuvwxyz"
echo ${var:$(shuf -i 0-$((${#var}-1)) -n1):1}
Run Code Online (Sandbox Code Playgroud)
The idea here is to pick a letter from the string var by using the pattern expansion ${var:m,n} where you pick a substring starting at m of length n. The length is set to 1 and the starting position is defined by the command shuf -i 0-$((${#var}-1) which shuffles a range between 0 and ${#var}-1 where ${#var} is the string length of the variable var.
Why not using RANDOM:
The random variable RANDOM generates a pseudo-random number between 0 and 32767. This implies that if you want to generate a random number between 0 and n, you cannot use the mod. The problem here is that the first 32768%n numbers will have a higher chance to be drawn. This is easily seen with the following script :
% 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
Run Code Online (Sandbox Code Playgroud)
Another classic approach is to map the range of the random number generator onto the requested range by scaling the random value as n*RANDOM/32768. Unfortunately, this only works for a random number generator that generate real numbers. RANDOM generates an integer. The integer scaling essentially shuffles the earlier problem:
% 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
Run Code Online (Sandbox Code Playgroud)
If you want to use RANDOM, the best way is to skip the values which are not needed, this you can do with a simple while loop
var="abcdefghijklmnopqrstuvwxyz"
n=${#var}
idx=32769; while (( idx >= (32768/n)*n )); do idx=$RANDOM; done
char=${var:$idx:1}
Run Code Online (Sandbox Code Playgroud)
注意:您可能会永远陷入 while 循环中。
评论:我们不评论背后的随机数生成器有多好RANDOM。我们所做的就是引用来源中的评论:
源 bash 4.4.18 (
variables.c)Run Code Online (Sandbox Code Playgroud)var="abcdefghijklmnopqrstuvwxyz" echo ${var:$(shuf -i 0-$((${#var}-1)) -n1):1}
您还可以shuf以更简洁的方式使用。
基本上,您将字符串拆分为每行上的一个字符,然后使用shuf正常的方式:
用于fold分割
echo "abcdefghijklmnopqrstuvwxyz" | fold -w1 | shuf -n1
Run Code Online (Sandbox Code Playgroud)
用于grep分割
echo "abcdefghijklmnopqrstuvwxyz" | grep -o . | shuf -n1
Run Code Online (Sandbox Code Playgroud)