我正在尝试创建一个 bash 脚本来显示当天的单词(每天随机)。我有一个字典文件,每一行都有一个单词及其定义。
我想用它date
来获得每一天的独特价值。像这样
today=$(date '+%Y%m%d') # will return 20160616 (for today)
现在我想使用这个值来生成一个行号,以便我从字典文件中获取。
我的字典很86036
长,所以我需要转换为和$today
之间的值。1
86036
做这个的最好方式是什么?
答案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
shell 的变量来获取随机数,但首先使用今天的日期为随机数生成器提供种子(如果自午夜以来未使用该脚本)。然后从文件中选择该行。
换句话说(下面的 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
使用今天的日期重新播种并将今天的日期写入文件中。
编辑#1:我还必须存储最后使用的随机数,因为$RANDOM
它是当前 shell 的本地随机数。否则,播种不会延续到脚本的下一次调用。我将其存储在一个单独的“随机标记”文件中。您可能希望将其更改为仅使用一个文件来记录上次调用的日期和上次使用的随机数。
编辑#2:任何人都可以发现我的原始代码的问题吗?嗯,$RANDOM
永远不会超过 32767(16 位),并且据说该文件的行数比这个多。这意味着$RANDOM
单独使用可能不是一个好主意。这就是为什么我只是简单地将$RANDOM
其自身连接起来,生成更长的随机数。播种和“随机标记”文件不受此影响。
编辑#3:刚刚注意到OP请求“每次在特定的一天运行脚本时都使用同一行”(在评论中我的大脑昨晚深夜没有理解),这正是我的脚本所做的不是做(它给出了相同的行的顺序每天)。无论如何,我将我的解决方案留在这里,以防它帮助其他人解决保留$RANDOM
脚本调用之间状态的类似问题。
答案4
如果按顺序浏览文件不可接受,那么这是我的建议;需要 230 多年才能涵盖文件中的每一行,并且会在未来某个尚未确定的时间点背对背地重复单词。我通过在运行时计算“words”文件中的行数(单词定义)使其更加灵活,因此如果您在文件中添加或删除行,脚本将相应地调整。
#!/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