Lê dados de um pipe por um determinado período de tempo (em segundos)

4

Existe uma maneira fácil de criar um canal que dura apenas uma determinada quantidade de tempo de parede?

Eu quero ser capaz de tirar um "instantâneo" do que está sendo escrito STDOUT por um período de tempo (em vez de bytes ou linhas). Algo como

tail -f /var/log/whatever.log | pipe_for_seconds 10 > ./10_second_sample_of_log

Estou usando o BASH, mas se isso for muito mais fácil em algum outro shell, eu gostaria de ouvir sobre isso.

    
por Eli 27.03.2013 / 17:37

3 respostas

6

Gnu coreutils desde que a versão 7.0 possui um comando timeout :

timeout 10 tail -f /var/log/whatever.log 

Se você realmente precisar de um canal para o tempo limite para um procedimento ligeiramente diferente, envie para timeout :

tail -f /var/log/whatever.log | timeout 10 cat > ./10_second_sample_of_log

Note, entretanto, que matar alguma parte arbitrária de um pipeline pode causar problemas devido ao buffer , dependendo dos sinais e do comportamento do programa (essa questão cobre problemas relacionados: Desativar o buffer no tubo ). Ele normalmente irá alterar o código de saída do processo também.

Se você não tem (um recente) coreutils, este simples programa de tempo limite também funciona bem    link ou a abordagem perl:

tail -f /var/log/whatever.log | perl -n -e 'BEGIN{alarm 10; $|=1}; print;'

(Observe que $|=1 desativa o buffer de saída, isso é para evitar perda de saída no pipeline, conforme mencionado acima.)

O pacote (um pouco antigo) Netpipes também tem um comando timelimit (que você pode ainda encontrar em alguns sistemas Linux).

Esta pergunta semelhante tem mais algumas opções: Como introduzir o tempo limite para scripts de shell?

    
por 27.03.2013 / 18:08
4

Isso deve funcionar:

tail -f /var/log/whatever.log > ten_second_sample & sleep 10 && kill $!

Lance tail em segundo plano e em paralelo sleep 10 segundos e depois disso kill $! , que é o PID do último processo em segundo plano iniciado.

    
por 27.03.2013 / 17:53
0

Basear-se em matar o PID pode não ser confiável se tail -f sair muito cedo (por exemplo, arquivo não existe). Outra solução mais genérica, que também irá parar o "kill timer" se for feito antes do tempo limite ser usado wait e kill %% ( %% indica o trabalho mais recente iniciado pelo bash - usando & ).

A construção será semelhante a esta (em que sleep 2 é qualquer comando personalizado com parâmetros etc e o tempo limite é definido para 10 segundos):

# Your command that may take some (or not) time to execute:
sleep 2 &
# Set a timer that kills the last started job
sleep 10 & wait -n 1; kill %%; wait -n 1

Esta construção aguardará até que o tempo limite seja atingido ou o comando personalizado seja executado (neste caso, o sleep 2 sempre será interrompido primeiro). Então, respectivamente, o comando ou o tempo limite será eliminado com kill %% . Em seguida, o último wait -n 1 bloqueia a execução adicional até que o comando ou o tempo limite seja realmente eliminado (isso é opcional, mas provavelmente desejado).

Nota. Comandos anteriores executados em paralelo não serão afetados, como é necessário e esperado.

Exemplo

Um exemplo mais prático com a leitura de uma linha de um pipe nomeado com tempo limite, mas para o qual o sinalizador de tempo limite de read não pode ser usado:

# Custom bash function to set timeout in seconds for the most recently started job (parallel execution).
timeout() { sleep $1 & wait -n 1; kill %%; wait -n 1 }

# Example 1:
mkfifo /tmp/test
read ln </tmp/test &
timeout 10

Como não há nada gravado em /tmp/test , o tempo limite será atingido após 10 segundos. Para mostrar que, mesmo quando o comando terminar imediatamente, o cronômetro de parada será interrompido:

# Example 2:
mkfifo /tmp/test
echo "Hello" >/tmp/test &
ln="$(read ln </tmp/test && echo -e "$ln" & timeout 10)"
echo "Line was read: $ln"

Observe com relação ao uso prático do pipe nomeado. Como read ln é executado em um subshell, ele não pode ser acessado no script (pai). Portanto, a linha é impressa com echo para armazená-la em uma variável (diferente) ln .

    
por 10.04.2017 / 14:14