POSIX equivalente para GNU timeout?

14

O timeoutcomando GNU coreutils é extremamente útil para determinadas situações de script, permitindo usar a saída de um comando se for rápido para executar e ignorá-lo se levar muito tempo.

Como posso aproximar o comportamento básico de timeoutusar apenas utilitários especificados POSIX?


(Eu estou pensando que pode envolver uma combinação de wait, sleep, kille quem sabe mais o quê, mas talvez eu estou faltando uma abordagem mais fácil.)

Curinga
fonte
3
Consulte Tempo limite em um script de shell , mas não considero isso uma duplicata porque solicitei portabilidade para sistemas pré-POSIX e tinha o requisito de preservar stdin e stdout, que algumas soluções envolvendo processos em segundo plano descartam.
Gilles 'SO- stop be evil'
command & pid=$! ; sleep 5 && kill $pid
21416
1
O @Pandya não introduz uma pequena condição de corrida, em que se commandterminar rapidamente, há uma pequena chance de pidser reutilizado por outro processo iniciado antes do killcomando ser executado? Eu não gostaria que no código de produção ....
Wildcard
Você pode explicar mais sobre o problema subjacente que está tentando resolver? Por que não apenas compilar o timeoutprograma e usá-lo?
James Youngman
2
@ JamesYoungman, acho que você não está familiarizado com a escrita de scripts para portabilidade . Pedir uma maneira de fazer algo compatível com POSIX (ou especificado com POSIX) implica que ele deve ser portátil. Compilar o código fonte em um binário não é enfaticamente portátil, e, dependendo de suas políticas de segurança da empresa, você não pode ter um compilador instalado em tudo em um servidor de produção.
Curinga

Respostas:

2

Minha abordagem seria esta:

  • Executar comando como processo em segundo plano 1

  • Execute o "watchdog timer" como processo em segundo plano 2

  • Configure um manipulador para interceptar um sinal de terminação no shell pai

  • Aguarde a conclusão de ambos os processos. O processo que termina primeiro envia o sinal de término ao pai.

  • O manipulador de interceptação dos pais mata os dois processos em segundo plano por meio do controle de tarefas (um deles já foi finalizado por definição, mas essa interrupção será uma operação inofensiva porque não estamos usando PIDs, veja abaixo)

Tentei contornar a possível condição de corrida abordada nos comentários usando os IDs de controle de trabalho do shell (que seriam inequívocos nessa instância do shell) para identificar os processos em segundo plano a serem eliminados, em vez dos PIDs do sistema.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Resultado para TIMEOUT=10(o comando termina antes do cão de guarda):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Resultado para TIMEOUT=1(o watchdog termina antes do comando):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Resultado para TIMEOUT=5(watchdog e comando terminam "quase" simultaneamente):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution
Guido
fonte
Isso não é compatível com POSIX, pois (a) a definição da função deve ser cleanup() { ...; }e (b) o controle de tarefas é um recurso bash não especificado pelo POSIX (embora o ksh e outros o tenham também). Bom roteiro, no entanto!
Wildcard
Você também pode fazer melhor com timeout="$1"; shifte, em (exec "$@"; echo "Command completed"; kill $$)vez de reexplicar a lista de argumentos.
Wildcard