Preciso executar vários scripts de shell sequencialmente (s1.sh, s2.sh, s3.sh) com o seguinte requisito:
- Se um script não produzir nenhuma saída (stdout ou stderr) por 1 minuto, ele deve ser encerrado
- Após o término, o próximo script na sequência deve ser executado automaticamente
- Isso deve continuar até que todos os scripts sejam processados
Situação atual:
- Os scripts não saem sozinhos quando sua tarefa é concluída
- Atualmente, monitoro manualmente cada script e pressiono Ctrl-cquando não percebo nenhuma saída por um tempo
- É preciso automatizar esta intervenção manual
Exemplo de fluxo de trabalho:
- Execute s1.sh
- Se s1.sh ficar em silêncio por 1 minuto → encerre-o → execute s2.sh
- Se s2.sh ficar em silêncio por 1 minuto → encerre-o → execute s3.sh
- ...e assim por diante
Abordagem potencial que estou considerando: Estou pensando em redirecionar stdout e stderr para um arquivo e verificar o tempo de modificação do arquivo a cada segundo. Se o arquivo não tiver sido gravado por 1 minuto, eu poderia assumir que o script não tem saída e matá-lo. No entanto, não tenho certeza se essa abordagem é viável ou se há soluções melhores.
Alguém implementou algo parecido? Obrigado.
Você poderia fazer algo como:
Então:
Onde executamos o comando
stdbuf -oL
(como encontrado em sistemas GNU ou FreeBSD, pelo menos) para (esperar) obter buffer de linha, pois, caso contrário, muitos comandos alternam para buffer de bloco quando sua saída é um pipe.sh
é usado para passar seu pid, que se tornará o do comando paraperl
.E canalizamos a saída para um script Perl embutido que redefine um alarme em cada linha de entrada e, quando o alarme dispara, mata esse pid.
Algumas limitações:
output_timeout 2 sh -c 'echo A; sleep 5; echo B'
, exceto emsh
implementações ondesleep
é embutido, isso matará o processo em execuçãosh
, mas não aquele em execuçãosleep
.stdbuf
não é eficaz em todos os comandos, por exemplo, não nos construídos estaticamente ou setuid/setgid ou aqueles que não usam stdio para buffer ou ajustam explicitamente esse buffer por si próprios.perl
a saída do for desacelerada (por exemplo, porque foi redirecionada para um leitor lento), ele pode acabar matando seu alimentador, mesmo que haja entrada para ser lida nele.perl
lê sua entrada uma linha por vez, então se por exemplo o alimentador nunca escreve nenhum caractere de nova linha, ele será morto após 60 segundos independentemente de quão rápido ele escreve sua saída. Isso poderia ser tratado lendo a entrada um byte por vez (como adicionando$/ = \1
depois de ter lido o pid).pipefail
opção agora é POSIX, mas você ainda encontra algumassh
implementações (notavelmente dash) que ainda não a suportam. Mude para ksh/zsh/bash se esse for o caso.Um ponto de partida poderia ser usar um
while read -t 60
loop para verificar se ascript
saída é algo no tempo dado. Agora, o problema é que você precisará obter o PID dissoscript
quando quiser matá-lo. No exemplo a seguir, eu trabalho em torno do problema usando um sinal, mas provavelmente há maneiras mais inteligentes de fazer isso: