Como influenciar um bash while loop com uma variável global temporizada?

2

Eu tenho um loop while em um script bash , que deve fazer algo diferente no início e a cada intervalo de 5 segundos. Qualquer loop anterior pode ser concluído. O intervalo de 5s é indicado pela variável global do_different definida pela função heartbeat . Uma complicação adicional é que um loop while normal é concluído em um período de tempo desconhecido (simplificado com RANDOM no script abaixo). Usar cron não é uma opção, nem é o momento do processo aleatório.

Eu já tentei sem sucesso usar um pipe, assim como a substituição do processo. Todo o roteiro pode ser reformulado.

#!/bin/bash

function heartbeat {
    do_different=true
    while sleep 5s
    do
        do_different=true
    done
}

heartbeat &

while true
do
    if $do_different
    then
        echo 'Something different'
        do_different=false
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "${r}s"
        i=$((i + r))
        echo "$i"
    fi
done
    
por Serge Stroobandt 08.07.2015 / 18:59

4 respostas

1

Aqui está o código que estou executando agora, com base na resposta da airfishey com algumas correções.

#!/bin/bash

t_lastdue=$(date --date='5seconds ago' +%s)

while true
do
    t_now=$(date +%s)
    t_elapsed=$((t_now - t_lastdue))
    if [ $t_elapsed -ge 5 ]
    then
        echo 'Something different'
        t_lastdue=$((t_lastdue + 5))
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "${r}s"
        i=$((i + r))
        echo "$i"
    fi
done
    
por 08.07.2015 / 23:19
3

Primeiro de tudo, caso não seja óbvio, o script na pergunta falha porque heartbeat é executado em um processo filho e, portanto, não pode alterar as variáveis do shell na memória do shell pai.

Aqui está uma abordagem mais próxima do espírito da tentativa do OP:

#!/bin/bash

trap 'do_different=true' USR1

heartbeat() {
    while sleep 5
    do                  # If the parent process has gone away, the child should terminate.
        kill -USR1 "$$"  ||  exit
    done
}

heartbeat &
heartbeat_pid=$!
trap 'kill "$heartbeat_pid"' 0

do_different=true

while true
do
    if "$do_different"
    then
        echo 'Something different'
        do_different=false
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "$r"
        i=$((i + r))
        echo "$i"
    fi
done

O heartbeat modificado envia sinais SIGUSR1 para o processo de shell principal (pai). Isso e SIGUSR2 são reservados para uso do usuário / aplicativo (e nunca deve ser gerado pelo sistema). O comando trap permite que um script de shell capture sinais. O comando trap 'do_different=true' USR1 informa ao shell para capturar o sinal SIGUSR1 (que chega a cada cinco segundos) e defina o sinalizador do_different quando ocorrer.

heartbeat_pid , obviamente, é o ID do processo (PID) do processo filho heartbeat . O comando trap 'kill "$heartbeat_pid"' 0 define uma ação para ocorrer mediante "recibo" do pseudo-sinal 0, que se refere à saída do script. Pense nisso como a casca colocando uma nota na porta dizendo "Depois de sair, lembre-se de comprar mantimentos a caminho de casa". Esta ação será invocada se o script chegar ao final ou executar uma declaração exit (nenhum dos quais pode acontecer com este script, já que é um loop infinito), ou se é terminado por um sinal de interrupção (SIGINT, que é gerado por Ctrl + C ). Esta é uma rede de segurança; o processo heartbeat já está escrito para terminar quando o processo pai desaparece.

    
por 08.07.2015 / 22:37
1

Eu usaria o utilitário date para obter o tempo atual em segundos.

#!/bin/bash
lastTime=-5

while true
do
    currentTime=$(date +%s)
    elapsedTime=$((currentTime - lastTime))
    if [[ $elapsedTime -ge 5 ]]
    then
        echo 'Something different'
        lastTime=$currentTime
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep ${r}s
        i=$((i + r))
        echo $i
    fi
done

Editar: alterou o valor inicial de lastTime para que também "faça algo diferente" no início.

    
por 08.07.2015 / 20:15
-1

Como você está usando bash mesmo assim, deve usar $SECONDS :

#!/bin/bash
while [ "$SECONDS" -lt 5 ] || {
      SECONDS=$((i=0))
      echo Something different
};do  sleep "$((r=(1+RANDOM)%3))"
      echo  "$((i+=r))"
done

De man bash :

$SECONDS

  • This variable expands to the number of seconds since the shell was started. Assignment to this variable resets the count to the value assigned, and the expanded value becomes the value assigned plus the number of seconds since the assignment.

E aqui está como você pode fazer isso e lidar com drift :

S=SECONDS
while   [ "$(($S<5||(i=0*($S-=5))))" -ne 0 ] ||
        echo Something different
do      sleep "$((r=(1+RANDOM)%3))"
        echo  "$((i+=r))"
done
    
por 09.07.2015 / 09:30