シェルスクリプトを記述しようとしています。アイデアは、テキストファイルからランダムに1行を選択し、Ubuntuデスクトップ通知として表示することです。
ただし、スクリプトを実行するたびに異なる行を選択する必要があります。これを行う解決策はありますか?スクリプト全体は必要ありません。単純なことだけです。
シェルスクリプトを記述しようとしています。アイデアは、テキストファイルからランダムに1行を選択し、Ubuntuデスクトップ通知として表示することです。
ただし、スクリプトを実行するたびに異なる行を選択する必要があります。これを行う解決策はありますか?スクリプト全体は必要ありません。単純なことだけです。
回答:
shuf
ユーティリティを使用して、ファイルからランダムな行を印刷できます
$ shuf -n 1 filename
-n
:印刷する行数
例:
$ shuf -n 1 /etc/passwd
git:x:998:998:git daemon user:/:/bin/bash
$ shuf -n 2 /etc/passwd
avahi:x:84:84:avahi:/:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
n
、印刷する行数を示す必要はありません。(つまり、1行だけにするか2行にするか)。行番号ではありません(つまり、1行目、2行目)。
date +%S
)を変数xに保存し、テキストファイルのhead
and tail
コマンドを使用してx番目の行を選択します。とにかくあなたの方法はより簡単です。おかげで
shuf
coreutilsにあるため、デフォルトで使用可能です。注:入力ファイルをメモリにロードします。それを必要としない効率的なアルゴリズムがあります。
楽しみのためだけに、ここにある純粋なbashのソリューションを使用しないshuf
、sort
、wc
、sed
、head
、tail
またはその他の外部ツール。
shuf
バリアントに対する唯一の利点は、純粋なbashであるため、わずかに高速であることです。私のマシンでは、1000行のファイルの場合、shuf
バリアントは約0.1秒かかりますが、次のスクリプトは約0.01秒かかりshuf
ます。
正直shuf
なところ、高効率が重要な懸念事項でない限り、私はまだ解決策を求めています。
#!/bin/bash
FILE=file.txt
# get line count for $FILE (simulate 'wc -l')
lc=0
while read -r line; do
((lc++))
done < $FILE
# get a random number between 1 and $lc
rnd=$RANDOM
let "rnd %= $lc"
((rnd++))
# traverse file and find line number $rnd
i=0
while read -r line; do
((i++))
[ $i -eq $rnd ] && break
done < $FILE
# output random line
printf '%s\n' "$line"
shuf
とにかく使用する方がはるかに優れています。それを考えると、shuf
以前に書いたように、純粋なbashがを使用するよりも実際に効率的であるとは思いません。外部ツールを起動するときに最も小さい(一定の)オーバーヘッドが発生する場合がありますが、解釈されたbashよりも速く実行されます。したがって、shuf
確かにスケーリングが向上します。それで、スクリプトが教育目的に役立つとしましょう:それができることを見るのは素晴らしいことです;)
shuf
のGNU Coreutils –特定(たとえば、FreeBSD 10.0ではありません)。sort -R
は移植可能ですが、別の(関連する)問題を解決します。複数の行として表示される文字列は、一度だけ表示される文字列と等しい確率を持ちます。(もちろん、wc
その他のユーティリティも使用できます。)ここでの主な制限は、これが32768行目以降に何も選択しないことです(そして、やや早くランダムになりません)。
$((RANDOM<<15|RANDOM))
0..2 ^ 30-1です。@JFSebastianそれshuf
はsort -R
、ではなく、より頻繁な入力に偏っています。のshuf -n 1
代わりに置き、sort -R | head -n1
比較します。(ところで10 ^ 3回の反復より早く10 ^ 6との違いを示すためにまだかなり十分に超えています。)も参照してください粗く、より視覚的なデモと、それはすべての文字列は、高周波あり、大きな入力で動作を示す愚かさのこのビットを。
dieharder
すべてゼロのようです。これが単に私の側の奇妙な間違いではないと仮定すると、それが間違いなくランダムではない理由を確実に説明するでしょう!while echo $(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 )); do :; done | perl -ne 'print pack "I>"' > out
しばらく実行out
してから16進エディタで内容を調べると、見栄えの良いデータが得られますか?(または、好きなように表示します。)私はすべてゼロを取得しRANDOM
、犯人ではありません:に置き換える$(( RANDOM << 17 | RANDOM << 2 | RANDOM >> 13 ))
と100
、すべてゼロを取得します。
fileがあるとしますnotifications.txt
。ランダムジェネレーターの範囲を決定するには、行の総数をカウントする必要があります。
$ cat notifications.txt | wc -l
変数に書き込みましょう:
$ LINES=$(cat notifications.txt | wc -l)
今から数生成する0
に$LINE
我々が使用するRANDOM
変数を。
$ echo $[ $RANDOM % LINES]
変数に書き込みましょう:
$ R_LINE=$(($RANDOM % LINES))
ここで、この行番号を印刷するだけです。
$ sed -n "${R_LINE}p" notifications.txt
RANDOMについて:
RANDOM Each time this parameter is referenced, a random integer between
0 and 32767 is generated. The sequence of random numbers may be
initialized by assigning a value to RANDOM. If RANDOM is unset,
it loses its special properties, even if it is subsequently
reset.
ファイルの行番号が32767未満であることを確認してください。箱から出して動作するより大きなランダムジェネレータが必要な場合は、これを参照してください。
例:
$ od -A n -t d -N 3 /dev/urandom | tr -d ' '
LINES=$(wc -l < file.txt); R_LINE=$((RANDOM % LINES)); sed -n "${R_LINE}p" file.txt
入力ファイルまたは標準入力からランダムな行を選択するPythonスクリプトを次に示します。
#!/usr/bin/env python
"""Usage: select-random [<file>]..."""
import random
def select_random(iterable, default=None, random=random):
"""Select a random element from iterable.
Return default if iterable is empty.
If iterable is a sequence then random.choice() is used for efficiency instead.
If iterable is an iterator; it is exhausted.
O(n)-time, O(1)-space algorithm.
"""
try:
return random.choice(iterable) # O(1) time and space
except IndexError: # empty sequence
return default
except TypeError: # not a sequence
return select_random_it(iter(iterable), default, random.randrange)
def select_random_it(iterator, default=None, randrange=random.randrange):
"""Return a random element from iterator.
Return default if iterator is empty.
iterator is exhausted.
O(n)-time, O(1)-space algorithm.
"""
# from /programming//a/1456750/4279
# select 1st item with probability 100% (if input is one item, return it)
# select 2nd item with probability 50% (or 50% the selection stays the 1st)
# select 3rd item with probability 33.(3)%
# select nth item with probability 1/n
selection = default
for i, item in enumerate(iterator, start=1):
if randrange(i) == 0: # random [0..i)
selection = item
return selection
if __name__ == "__main__":
import fileinput
import sys
random_line = select_random_it(fileinput.input(), '\n')
sys.stdout.write(random_line)
if not random_line.endswith('\n'):
sys.stdout.write('\n') # always append newline at the end
アルゴリズムはO(n)時間、O(1)スペースです。32767行を超えるファイルに対して機能します。入力ファイルをメモリにロードしません。各入力行を一度だけ読み取ります。つまり、任意の大きな(ただし有限の)コンテンツをパイプで入力できます。ここだアルゴリズムの説明が。
Malte Skoruppaなどが行った作業には感心しましたが、これを行うためのはるかに単純な「純粋なbash」方法があります。
IFS=$'\012'
# set field separator to newline only
lines=( $(<test5) )
# slurp entire file into an array
numlines=${#lines[@]}
# count the array elements
num=$(( $RANDOM$RANDOM$RANDOM % numlines ))
# get a (more-or-less) random number within the correct range
line=${lines[$num]}
# select the element corresponding to the random number
echo $line
# display it
一部の人が指摘したように、$ RANDOMはランダムではありません。ただし、32767行のファイルサイズ制限は、必要に応じて$ RANDOMを文字列化することで克服されます。