日付シード値の範囲内で行番号/「インデックス」を生成する

日付シード値の範囲内で行番号/「インデックス」を生成する

私は今日の単語(毎日ランダムに)を表示するbashスクリプトを作成しようとしています。辞書ファイルがあり、各行には単語とその定義があります。

date私はそれを使用して毎日のユニークな価値を得たいと思います。このように

today=$(date '+%Y%m%d') # will return 20160616 (for today)

これで、この値を使用して辞書ファイルから取得できる行番号を生成したいと思います。

私の辞書が86036長すぎてからの間の値に変換する必要があります$today186036

これを行う最良の方法は何ですか?

答え1

少し異なる解決策:cronジョブを使用して、毎日テキストファイルの行を混在させます。その後、スクリプトは最初の行を選択します。

Cronタスク(sortランダムにデータを「並べ替える」ことができるタスクが必要-R):

0 0 * * * sort -R -o wotd_data.txt wotd_data.txt

またはcron理解している場合@daily(参照man 5 crontab):

@daily sort -R -o wotd_data.txt wotd_data.txt

スクリプト:

head -n 1 wotd_data.txt

wotd_data.txt明らかにフルパスが必要です。

答え2

モジュロ数学!

$ today=`date +%Y%m%d`
$ echo $(( today % 86036 + 1 ))
28193

wc -l that file...ファイルの長さが変わると、86036が発生する可能性があります...

答え3

シェルの変数を使用してRANDOM乱数を取得します。ただし、最初に乱数ジェネレータに今日の日付を入力します(スクリプトが真夜中以降に使用されていない場合)。次に、ファイルから適切な行を選択します。

つまり(下のBash)...

wotd_data="wotd_data.txt"

stamp="$HOME/.wotd-stamp"
stamp_random="$HOME/.wotd-random"

date_now=$( date +"%Y%m%d" )

if [ -f "$stamp" ]; then
    date_last=$( <"$stamp" )
else
    date_last=0
fi

if [ "$date_last" != "$date_now" ]; then
    RANDOM="$date_now"
    echo "$date_now" >"$stamp"
else
    RANDOM=$( <"$stamp_random" )
fi

number=$RANDOM
echo $number >"$stamp_random"

number=$RANDOM$RANDOM  # See the "Edit #2" note below

data_length=$( wc -l <"$wotd_data" )

line=$(( 1 + ( number % data_length ) ))

sed -n "${line}p" "$wotd_data"

これは、ユーザーのタイムスタンプファイルを使用して$HOME最後にコマンドを実行した時刻を追跡します。今日でない場合は、$RANDOM今日の日付を使用して再シードし、今日の日付をファイルに書き込みます。

$RANDOM編集#1:現在シェルにローカルなので、最後に使用したノンスも保存する必要がありました。それ以外の場合は、次のスクリプトを呼び出すとシードは続行されません。私はこれを別の「ランダムタグ」ファイルに保存します。ファイルを使用して、最後の通話日と最後に使用されたランダム番号を記録するように変更できます。

編集#2:誰も私のソースコードで問題を発見できますか?まあ、$RANDOM32767(16ビット)を超えてはいけませんし、ファイルにはそれよりも多くの行があるという。これは$RANDOM、単独で使用するのが悪いことを意味します。そのため、単に$RANDOMそれ自体で接続して、より長い乱数を生成します。シードファイルと「ランダムにマークされた」ファイルは影響を受けません。

編集#3:OPが「特定の日にスクリプトを実行するたびに同じ行を使用してください」と要求したことに気づきました(昨夜遅く私の脳が理解していないコメントで)。これが私のスクリプトが行うことです。いいえはい(同じ結果を提供します行の順序毎日)。とにかく、$RANDOMスクリプト呼び出し間の状態を維持する同様の問題がある他の人に役立つように、ここに私の解決策を残します。

答え4

ファイルを順番に確認することができない場合は、次のように提案します。ファイルのすべての行を処理するには230年以上かかり、将来未定の時点で単語が連続して繰り返されます。実行時に、「単語」ファイルの行数(単語定義)を計算して、より柔軟にしました。したがって、ファイルから行を追加または削除すると、スクリプトはそれに応じて調整されます。

#!/usr/bin/env bash

# number of days since 1970-01-01 00:00:00
seed=$(( ($(date +%s) / 86400) ))
# initialize RNG to this seed
RANDOM=$seed
nwords=$(wc -l < words)
# generate two random numbers (0 .. 32767), multiply them,
# modulo nwords, plus 1 -> range 1..86036
r=$(( ((RANDOM * RANDOM) % nwords) + 1 ))
# print that line from the 'words' file
sed -n "${r}p" words

関連情報