Continuamente atualizando uma variável em segundo plano

3

Estou tentando fazer com que um script execute continuamente um loop que atualize uma variável em segundo plano, para que a variável esteja atualizada sempre que for usada. Depois de procurar por quase uma hora, não consegui nada, o que é surpreendente, porque parece que alguém teria querido fazer isso antes de mim.

Meu script, que deve definir $SESSIONLENGTH para a string formatada que é a duração da sessão ...

#!/bin/bash

while true
do

SECONDSINSESSION=$SECONDS

SECONDSINMINUTE=60
SECONDSINHOUR=3600
SECONDSINDAY=86400

DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")

SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."

sleep 1

done &

Portanto, quando eu verificar que ele está sendo executado com jobs (que é) e use echo $SESSIONLENGTH , o valor não será atualizado ou não estará presente.

Alguém pode me ajudar com isso?

QUICK-EDIT: Também deve ser observado que esse script precisa ser executado em source ou . .

    
por Trevor Sears 25.07.2016 / 06:25

4 respostas

1

Tendo resolvido o problema do OP, pensei em explicar o problema e a resolução escolhida.

Tendo escrito o script básico, ele estava sendo testado usando o recurso source (ou é alias . ). Nesta situação, o script funcionou bem. Isso funciona porque as variáveis do shell estão dentro do processo do shell.

Quando executado como um processo em segundo plano (ou apenas chamado como um comando), o script é executado como um processo separado, bifurcado a partir do processo do shell. O processo de shell não sabe nada sobre as variáveis no novo processo, explicando por que

the value is either not updated, or not present at all.

Existem muitas maneiras pelas quais os dois processos podem se comunicar, mas o que escolhemos no bate-papo foi usar arquivos temporários. No prompt de comando ou em outro script, o OP pode apenas cat myfile para ver o valor mais recente da variável.

Esta abordagem é adequada neste caso porque

  • corresponde ao nível atual de conhecimento do OP
  • a quantidade de dados é baixa
  • as expectativas de desempenho são baixas.

Notamos que o sistema de arquivos está lidando com problemas de simultaneidade e, nas circunstâncias, o OP não está preocupado com as condições de corrida e outros problemas de simultaneidade.

Se apenas uma cópia do script puder ser executada por vez, o nome do arquivo poderá ser uma constante. Caso contrário, um arquivo temporário deve ser alocado; a maioria das shells tem um meio de alocar arquivos temporários. Se este fosse mais um cenário de produção, o script deveria colocar algum esforço na limpeza de arquivos antigos.

    
por 25.07.2016 / 11:23
1

Como você está usando bash , você pode trap o DEBUG signal, para recalcular SESSIONLENGTH antes de cada comando:

trap 'source /path/to/sessionlength' DEBUG

O script sessionlength originado precisa ser apenas o conteúdo do seu loop, sem as partes while e sleep em torno dele:

SECONDSINSESSION=$SECONDS

SECONDSINMINUTE=60
SECONDSINHOUR=3600
SECONDSINDAY=86400

DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")

SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."

bash também oferece expansão aritmética , para evitar a desova 6 expr processa cada vez usando

DAYSINSESSION=$((SECONDSINSESSION/SECONDSINDAY))

etc.

    
por 25.07.2016 / 11:28
1

Existem algumas coisas técnicas erradas com o seu script / abordagem, mas esquecendo os detalhes técnicos por um momento, em um nível intuitivo, o que você está tentando fazer é colocar algum processo de contagem em movimento, então em algum momento no futuro - você quer interativamente consultar o processo para ver como as coisas estão indo tentando referenciar diretamente o valor da variável.
Você não pode fazer isso no bash porque o bash shell é um interpretador de linguagem síncrono , o que significa que enquanto o processo bash está executando o loop while você não pode interrompê-lo e dizer o equivalente de programação :

"hey bash, stop what you are doing, then go off and access some piece of memory (the $SESSIONLENGTH variable) and then print out its value to screen, then carry on whatever you were doing"

O que você está tentando fazer é resolvido pela simultaneidade e é o tipo de coisa que você faz em assíncrono (nodejs, nginx) plataformas ou processos de encadeamento único executando um loop de eventos como o vim, ou javascript em um navegador da web.

A partir do artigo sobre simultaneidade da Wikipedia

A concurrent system is one where a computation can advance without waiting for all other computations to complete; where more than one computation can advance at the same time

Eu adicionaria o qualificador ou pelo menos apareceria para o usuário como se mais de um cálculo estivesse avançando ao mesmo tempo.
Então, o que você pode fazer em bash? você pode - como foi sugerido, obter o bash para enviar sua saída para outro lugar, ler os dados de um processo separado ou colocar essa versão modificada do seu código em um arquivo:

session_state.sh

for i in seq 1 2 3 4 5
do

    SECONDSINSESSION=$SECONDS

    SECONDSINMINUTE=60
    SECONDSINHOUR=3600
    SECONDSINDAY=86400

    DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
    DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
    HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
    HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
    MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
    SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")

    SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."

    echo $SESSIONLENGTH
    sleep 2

done 

, em seguida, fonte:

$ source session_state.sh 
0 days, 0 hours, 46 minutes, and 26 seconds.
0 days, 0 hours, 46 minutes, and 28 seconds.
0 days, 0 hours, 46 minutes, and 30 seconds.
0 days, 0 hours, 46 minutes, and 32 seconds.
0 days, 0 hours, 46 minutes, and 34 seconds.
0 days, 0 hours, 46 minutes, and 36 seconds.

, em seguida, consulte a variável

$ echo $SESSIONLENGTH
0 days, 0 hours, 46 minutes, and 36 seconds.

E você obterá a variável como ela existia em seu estado final - mas somente após o término do processo.
Uma solução teórica para o que você queria originalmente, em um shell síncrono de encadeamento único, seria

  • crie um segundo processo que monitore sua entrada de teclado,
  • tinha algum arranjo pré-estabelecido com o bash, então o segundo processo poderia
  • queue suas solicitações geradas pelo usuário para que, quando bash
  • terminou seu quadro / unidade de código atual, seria
    • verifique a fila para solicitações do usuário,
    • executá-los e, em seguida,
  • continue de onde parou pela última vez

em outras palavras, um loop de eventos .

    
por 25.07.2016 / 08:46
0

Pelo que entendi, você quer saber há quanto tempo seu script está sendo executado.

Se você criar um arquivo quando o script for iniciado, você terá a hora de início. Exemplo com hora de início de crond desde a época:

$ stat -c %Y /var/run/crond.pid
1447709241

Segunda desde o começo da crond:

$ echo $(( $(date +%s)-$(stat -c %Y /var/run/crond.pid) ))
21740420

Mostrado como data:

$ date -d @$(( $(date +%s)-$(stat -c %Y /var/run/crond.pid) )) +%F\ %T
1970-09-09 16:09:56

Se o programa que deveria estar lendo a variável não pode fazer o cálculo acima, então você deve escrever $ SESSIONLENGTH para um arquivo, e então o outro processo deve ler isso.

    
por 25.07.2016 / 14:46