2016-08-20 2 views
0

私は先月、これを書き上げたので不思議でした。argsからランダムなエントリを選択する方がいいですか?

#!/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. これを別のプログラムで1行で実行する方法はありますか?
  3. できるだけ凝縮するには?
+0

コマンドの呼び出し例を表示できますか?説明にカンマが表示されている場所は明確ではありません。 –

+1

btw、 'RANDOM_CMD'で使用しているパターンは、重大なバグを起こしやすくなります。リテラル引用符、エスケープされた空白などを含むコマンドは正しく処理されません。関連するベストプラクティスの説明については、[BashFAQ#50](http://mywiki.wooledge.org/BashFAQ/050)を参照してください。 –

+1

また、(非標準化された) 'seq'コマンドをbash組み込み' for((i = 0; i

答えて

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[@]}]} 
+0

$ {#words}は常に1です。$ {#words [@]} – frangio

+0

@frangio: '$ {#words}'は必ずしも1ではありません。これは '$ words'の文字数です。 'words'が配列の場合、' $ words'は配列の最初の要素です。 – rici

+0

あなたは正しい@riciです。 – frangio

2

コマンドライン引数の取り扱いは、私の心に、奇妙です。単に通常のコマンドライン引数を使用するのはなぜですか?

もちろん
#!/usr/bin/bash 
shuf -en1 "[email protected]" 

、あなただけshuf -en1を使用することができ、唯一の9つのキーストロークです:

$ 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に挿入します。文字列はカンマで区切られているため、空白(引数の連結によって自動的に挿入されるなど)は保持されます。空白を削除するには、コマンド展開を引用しないことでshufの結果を単語分割します。

+0

@charles:私はOPでの動作を模倣しようとしていました。私はすでに "奇妙な"と言いました。引用符を残すことは意図的であったが、あなたが言うように、事故を起こしやすい。 – rici

+0

ああ、そうです。 globingを無効にするために 'set -f'を使うかもしれないとしたら、OPのコードはglobを展開しました...(「引数を一緒に実行して、コンマで区切り、各分離後の値をグロブとして展開する」と答えた場合) –

+0

ありがとう私は 'shuf'というプログラムがあったことを知らなかったので、そこに質問#2があります。 –

0

shuf in coreutilsこれは正確ですが、コンマで区切られた単一の引数ではなく複数のコマンド引数を使用しています。

shuf -n1 -e arg1 arg2 ... 

-n1オプションは、1つの要素を選択すると言います。 -eオプションは、要素が(標準入力ではなく)引数として渡されることを示します。

スクリプトでは、カンマをスペースで置き換えるだけで$*になります。

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

これは、スペースが埋め込まれた要素では機能しません。

+0

なぜ 'shuf -n1 -e" $ @ "'?そうすれば、埋め込みスペース(またはグロブ - '*'はシャッフルされているものに現在のディレクトリ内のすべてのファイルの名前を入れます) –

+0

... riciがすでにそこに入っているのがわかります。 –

+0

これは間違いなく正しいことですが、私はちょうどOPのフォーマットに固執しました – frangio

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の場合、ゼロ除算エラーを防ぐために少なくとも1つの引数が提供されていることを確認する必要があります。コードは何らかの方法でデータをシャッフルするソリューションとは対照的に、データの移動を最小限に抑えます。それは、スクリプト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/$#)より大きい場合、別の乱数を生成する必要があります。

関連する問題