从args中选择随机条目的更好方法是什么?

时间:2016-08-20 04:22:06

标签: linux bash

只是想知道,因为我上个月鞭打了这个。

#!/usr/bin/bash

# Collects all of the args, make sure to seperate with ','
IN="$*"

# Takes everything before a ',' and places them each on a single line of tmp file
echo $IN | sed 's/,/\n/g' > /tmp/pick.a.random.word.or.phrase

# Obvious vars are obvious
WORDFILE="/tmp/pick.a.random.word.or.phrase"

# Pick only one of the vars
NUMWORDS=1

## Picks a random line from tmp file

#Number of lines in $WORDFILE
tL=`awk 'NF!=0 {++c} END {print c}' $WORDFILE`

# Expand random
RANDOM_CMD='od -vAn -N4 -tu4 /dev/urandom'

for i in `seq $NUMWORDS`
do
rnum=$((`${RANDOM_CMD}`%$tL+1))
sed -n "$rnum p" $WORDFILE | tr '\n' ' '

done

printf "\n"

rm /tmp/pick.a.random.word.or.phrase

主要是我问:

  1. 我需要一个tmp文件吗?
  2. 有没有办法在另一个程序的一行中执行此操作?
  3. 如何尽可能地浓缩?

4 个答案:

答案 0 :(得分:2)

在我看来,命令行参数处理是奇怪的。为什么不使用普通的命令行参数?这使问题变得微不足道:

#!/usr/bin/bash
shuf -en1 "$@"

当然,您可以使用shuf -en1,这只是九次击键:

$ shuf -en1 word another_word "random phrase"
another_word
$ shuf -en1 word another_word "random phrase"
word
$ shuf -en1 word another_word "random phrase"
another_word
$ shuf -en1 word another_word "random phrase"
random phrase

shuf命令行标志:

-e    Shuffle command line arguments instead of lines in a file/stdin
-n1   Produce only the first random line (or argument in this case)

如果你真的坚持一起运行参数然后用逗号分隔它们,你可以使用以下内容。与原始版本一样,如果参数中的某些单词可以进行全局展开,则会出现意外行为,所以我真的不推荐它:

#!/usr/bin/bash
IFS=, read -ra args <<<"$*"
echo $(shuf -en1 "${args[@]}")

第一行组合了参数,然后将逗号的结果拆分为数组args。 (要读取的-a选项。)由于字符串以逗号分隔,因此保留空格(例如,尽管由参数连接自动插入);为了删除空格,我通过不引用命令扩展来对shuf的结果进行分词。

答案 1 :(得分:0)

您可以使用shuff缩短脚本并删除临时文件。

#!/usr/bin/bash

# Collects all of the args, make sure to seperate with ','
IN="$*"

# Takes everything before a ',' and places them in an array
words=($(echo $IN | sed 's/,/ /g'))

# Get random indexi in range: 0, length of array: words
index=$(shuf -i 0-"${#words[@]}" -n 1)

# Print the random index
echo ${words[$index]}

如果您不想使用shuff,也可以使用$RANDOM

#!/usr/bin/bash

# Collects all of the args, make sure to seperate with ','
IN="$*"

# Takes everything before a ',' and places them in an array
words=($(echo $IN | sed 's/,/ /g'))

# Print the random index
echo ${words[$RANDOM % ${#words[@]}]}

答案 2 :(得分:0)

coreutils中的

shuf正是如此,但是使用多个命令参数而不是单个逗号分隔的参数。

shuf -n1 -e arg1 arg2 ...

-n1选项表示只选择一个元素。 -e选项表示元素将作为参数传递(而不是通过标准输入)。

您的脚本只需要用$*中的空格替换逗号。我们可以使用bash参数替换来执行此操作:

#!/usr/bin/bash
shuf -n1 -e ${*//,/ }

这不适用于带有嵌入空格的元素。

答案 3 :(得分:0)

是不是像在1和$#之间随机生成数字一样简单,只是回显相应的参数?这取决于你拥有的东西;你对'收集论点;确保用逗号分隔'不清楚,因为赋值对逗号没有任何作用 - 并且你没有显示你如何调用命令。

我只是简单地从问题中产生随机数生成:它在我的Mac上运行正常,在连续运行时生成值42,405,691和1,817,261,076。

n=$(( $(od -vAn -N4 -tu4 /dev/urandom) % $# + 1 ))
eval echo "\${$n}"

如果你真的下定决心,你甚至可以将它减少到一行:

eval echo "\${$(( $(od -vAn -N4 -tu4 /dev/urandom) % $# + 1 ))}"

eval的使用是安全的,因为它不涉及用户输入。在$#为0时,脚本应该检查是否提供了至少一个参数以防止被零除错误。代码执行数据移动的绝对最小值 - 与在某些方面对数据进行混洗的解决方案形成对比方式。

如果将其打包在脚本random_selection中,那么我可以运行:

$ bash random_selection Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Feb
$ bash random_selection Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Oct
$ bash random_selection Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Nov
$

如果参数的总数足够大,以至于你的参数空间不足,那么你需要再考虑一下,但现有代码中存在这种限制。

选择略微偏向于列表中较早的条目;你必须更好地拒绝非常接近范围内最大值的随机数。对于随机32位无符号值,如果它大于$# * (0xFFFFFFFF / $#),则应生成另一个随机数。