Gerar número de linha / “índice” dentro de um intervalo a partir de um valor de semente de data

4

Estou tentando criar um script bash para exibir uma palavra do dia (aleatória para cada dia). Eu tenho um arquivo de dicionário que em cada linha tem uma palavra e sua definição.

Gostaria de usar date para obter um valor exclusivo para cada dia. Assim

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

Agora eu gostaria de usar este valor para gerar um número de linha para eu pegar do arquivo do dicionário.

Meu dicionário é 86036 linhas, então preciso converter $today em um valor entre 1 e 86036 .

Qual é a melhor maneira de fazer isso?

    
por CS Student 16.06.2016 / 20:27

4 respostas

2

Uma solução um pouco diferente: embaralhe as linhas em seus arquivos de texto com um cron job todos os dias. Seu script, em seguida, escolhe a primeira linha.

Tarefa Cron (requer um sort que pode "classificar" dados aleatoriamente com -R ):

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

Ou, se o seu cron entender @daily (consulte man 5 crontab ):

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

Script:

head -n 1 wotd_data.txt

Caminhos completos para wotd_data.txt são obviamente obrigatórios.

    
por 17.06.2016 / 09:26
2

Matemática do módulo!

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

... possivelmente com o 86036 sendo wc -l that file no caso do tamanho do arquivo ser alterado ...

    
por 16.06.2016 / 20:32
2

Use a variável RANDOM do seu shell para obter um número aleatório, mas semear o gerador de números aleatórios com a data de hoje primeiro (se o script não tiver sido usado desde a meia-noite). Em seguida, escolha essa linha fora do arquivo.

Em outras palavras (Bash abaixo) ...

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"

Isso usa um arquivo de registro de data e hora no $HOME do usuário para acompanhar a última vez que o comando foi executado. Se não foi hoje, volte a semear $RANDOM com a data de hoje e escreva a data de hoje no arquivo.

EDIT # 1: Eu também tive que armazenar o último número aleatório usado desde $RANDOM é local para o shell atual. A semeadura não será transferida para a próxima invocação do script. Eu armazeno isso em um arquivo separado "selo aleatório". Você pode querer mudar isso para usar apenas um arquivo para a data da última chamada e o último número aleatório usado.

EDIT # 2: Alguém consegue identificar o problema com o meu código original? Bem, $RANDOM nunca será maior que 32767 (16 bits), e o arquivo teria mais linhas do que isso. Isso significa que talvez usar $RANDOM sozinho pode não ser uma boa ideia. É por isso que recorro a simplesmente concatenar $RANDOM consigo mesmo, gerando um número aleatório mais longo. A semeadura e o arquivo "selo aleatório" não são afetados por isso.

EDIT # 3: Só notei que o OP pedia "a mesma linha toda vez que o roteiro é executado em um dia em particular" (em um comentário que meu cérebro não digitou tarde ontem à noite), que é exatamente o que meu script não faz (dá a mesma seqüência de linhas todos os dias). Estou deixando minha solução aqui de qualquer maneira, caso isso ajude alguém com problemas semelhantes de preservar o estado de $RANDOM entre as chamadas de script.

    
por 16.06.2016 / 21:26
1

Se uma passagem sequencial pelo arquivo não for aceitável, então aqui está minha sugestão; Levará mais de 230 anos para cobrir todas as linhas do arquivo e repetirá palavras consecutivas em algum ponto ainda não determinado no futuro. Tornei um pouco mais flexível calculando o número de linhas (definições de palavras) no arquivo 'words' em tempo de execução, portanto, se você adicionar ou remover linhas do arquivo, o script será ajustado de acordo.

#!/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
    
por 17.06.2016 / 03:17