Chega de tempo excessivo no computador! É possível estabelecer limites de duração para processos, definindo um tempo máximo de execução através do comando `timeout`. Este guia prático demonstrará como aplicar restrições de tempo na execução de programas utilizando este recurso.
Qual a utilidade do comando `timeout`?
O comando `timeout` permite que você especifique um limite de tempo durante o qual um programa pode ser executado. Mas por que você gostaria de fazer isso?
Uma situação comum é quando você sabe exatamente por quanto tempo deseja que um processo rode. Por exemplo, você pode querer controlar o tempo de execução de um programa que gera logs ou coleta dados, evitando que os arquivos de registro consumam todo o espaço disponível no seu disco rígido.
Outro cenário é quando você não sabe ao certo quanto tempo um processo deve durar, mas tem certeza de que ele não deve ser executado indefinidamente. Muitas vezes, iniciamos processos, minimizamos a janela do terminal e acabamos nos esquecendo deles.
Alguns programas, mesmo utilitários simples, podem gerar tráfego de rede em níveis que prejudicam o desempenho da sua rede. Ou podem sobrecarregar os recursos de um dispositivo, diminuindo sua performance. (Sim, estou falando com você, `ping`.) Deixar esses programas rodando por longos períodos enquanto você está longe do computador não é uma boa prática.
O `timeout` faz parte do pacote GNU Core Utils, o que significa que sistemas operacionais Linux e similares, como o macOS, já vêm com ele integrado. Não há necessidade de instalação; você pode começar a usá-lo imediatamente.
Primeiros passos com o `timeout`
Vejamos um exemplo simples. O comando `ping`, por padrão, continua rodando até que você o interrompa com Ctrl+C. Se não o fizer, ele seguirá enviando pacotes indefinidamente.
ping 192.168.4.28
Com o `timeout`, podemos garantir que o `ping` não rodará indefinidamente, consumindo largura de banda e incomodando o dispositivo alvo.
O comando abaixo limita o tempo de execução do `ping` a 15 segundos:
timeout 15 ping 192.168.4.28
Após 15 segundos, o `timeout` encerra a sessão do `ping` e você retorna ao prompt da linha de comando.
Utilizando outras unidades de tempo
Note que não precisamos adicionar “s” após o número 15. O `timeout` assume que o valor está em segundos. Você pode adicionar um “s”, mas não fará diferença.
Para utilizar valores de tempo em minutos, horas ou dias, adicione “m”, “h” ou “d” respectivamente.
Para que o `ping` rode por três minutos, use o seguinte comando:
timeout 3m ping 192.168.4.28
O `ping` será executado por três minutos, até que o `timeout` seja atingido e encerre a sessão.
Limitando a captura de dados
Arquivos de captura de dados podem crescer muito rapidamente. Para evitar que esses arquivos se tornem muito grandes, limite o tempo de execução do programa de captura.
Neste exemplo, usamos o `tcpdump`, uma ferramenta de captura de tráfego de rede. Nas máquinas de teste utilizadas para este artigo, o `tcpdump` já estava instalado no Ubuntu Linux e Fedora Linux. No Manjaro Linux e Arch Linux, foi necessário instalá-lo com o seguinte comando:
sudo pacman -Syu tcpdump
Podemos executar o `tcpdump` por 10 segundos com suas opções padrão e redirecionar sua saída para um arquivo chamado `capture.txt` com este comando:
timeout 10 sudo tcpdump > capture.txt
(O `tcpdump` possui opções próprias para salvar o tráfego de rede capturado em um arquivo. Este é um exemplo rápido, pois o foco aqui é o `timeout`, não o `tcpdump`.)
O `tcpdump` começa a capturar o tráfego de rede e esperamos 10 segundos. Mas, mesmo após 10 segundos, o `tcpdump` continua rodando e o arquivo `capture.txt` continua a crescer. Precisaremos usar Ctrl+C para parar o `tcpdump`.
Verificando o tamanho do `capture.txt` com `ls`, vemos que ele atingiu 209K em poucos segundos. O arquivo estava crescendo muito rápido!
ls -lh capture.txt
O que aconteceu? Por que o `timeout` não interrompeu o `tcpdump`?
A explicação está nos sinais.
Enviando o sinal correto
Quando o `timeout` quer interromper um programa, ele envia o sinal SIGTERM. Este sinal é um pedido educado para que o programa seja encerrado. Alguns programas podem optar por ignorar o sinal SIGTERM. Nesses casos, precisamos ser mais incisivos.
Podemos instruir o `timeout` a enviar o sinal SIGKILL.
O sinal SIGKILL não pode ser “capturado, bloqueado ou ignorado” – ele sempre chega. O SIGKILL não pede educadamente para o programa parar. Ele “aparece” com um cronômetro e força o encerramento do processo.
Usamos a opção `-s` (sinal) para informar ao `timeout` que envie o sinal SIGKILL.
timeout -s SIGKILL 10 sudo tcpdump > capture.txt
Desta vez, assim que 10 segundos se passam, o `tcpdump` é interrompido.
Pedindo “educadamente” primeiro
Podemos instruir o `timeout` a tentar parar o programa usando SIGTERM, e apenas enviar SIGKILL se o SIGTERM falhar.
Para isso, usamos a opção `-k` (matar após). Esta opção precisa de um valor de tempo como parâmetro.
Neste comando, estamos dizendo ao `timeout` para permitir que o `dmesg` execute por 30 segundos e então encerrá-lo com o sinal SIGTERM. Se o `dmesg` ainda estiver rodando após 40 segundos, isso significa que o SIGTERM foi ignorado e o `timeout` enviará o SIGKILL para concluir o trabalho.
O `dmesg` é um utilitário que pode monitorar as mensagens do buffer do kernel e exibi-las na janela do terminal.
timeout -k 40 30 dmesg -w
O `dmesg` é executado por 30 segundos e para ao receber o sinal SIGTERM.
Sabemos que não foi o SIGKILL que interrompeu o `dmesg`, pois o SIGKILL sempre deixa uma mensagem na janela do terminal: “Morto”. Isso não aconteceu neste caso.
Recuperando o código de saída do programa
Programas bem comportados retornam um valor ao shell quando são encerrados. Este valor é conhecido como código de saída. Geralmente, ele é usado para informar ao shell ou a qualquer processo que iniciou o programa se houve problemas durante sua execução.
O `timeout` também fornece seu próprio código de saída, mas este pode não ser relevante para nós. Provavelmente, estamos mais interessados no código de saída do processo que o `timeout` está controlando.
Este comando permite que o `ping` seja executado por cinco segundos, testando a conectividade com um computador chamado Nostromo, presente na rede de testes utilizada para este artigo.
timeout 5 ping Nostromo.local
O comando é executado por cinco segundos e o `timeout` o encerra. Podemos verificar o código de saída com o comando:
echo $?
O código de saída é 124. Este é o valor que o `timeout` utiliza para indicar que o programa foi encerrado usando SIGTERM. Se o programa for encerrado com SIGKILL, o código de saída será 137.
Se interrompermos o programa com Ctrl+C, o código de saída do `timeout` será zero.
timeout 5 ping Nostromo.local
echo $?
Se a execução do programa terminar antes que o tempo limite seja atingido, o `timeout` pode passar o código de saída do programa para o shell.
Para isso, o programa precisa parar por conta própria (ou seja, não ser encerrado pelo `timeout`) e devemos utilizar a opção `–preserve-status`.
Se usarmos a opção `-c` (contagem) com um valor de cinco, o `ping` enviará apenas cinco solicitações. Se dermos ao `timeout` a duração de um minuto, o `ping` terminará por conta própria. Podemos verificar o código de saída com `echo`.
timeout --preserve-status 1m ping -c 5 Nostromo.local
echo $?
O `ping` completa suas cinco solicitações e termina. O código de saída é zero.
Para confirmar se o código de saída é do `ping`, podemos forçar o `ping` a gerar um código de saída diferente. Se tentarmos enviar solicitações para um endereço IP inexistente, o `ping` falhará com um código de erro. Podemos então usar `echo` para verificar se o código de saída é diferente de zero.
timeout --preserve-status 1m ping -c 5 NotHere.local
echo $?
O comando `ping` obviamente não consegue alcançar o dispositivo inexistente, então ele reporta o erro e finaliza. O código de saída é dois, que é o código de saída que o `ping` utiliza para erros gerais.
Estabelecendo limites básicos
O principal objetivo do `timeout` é estabelecer limites para a execução de programas. Se você corre o risco de seus arquivos de log consumirem todo o espaço do seu disco ou se você tem o hábito de esquecer ferramentas de rede rodando, utilize o `timeout` para deixar seu computador se autorregular.