Qual é uma maneira simples de executar um comando por 5 minutos? [duplicado]

66

Esta pergunta já tem uma resposta aqui:

Existe uma maneira fácil de permitir que um comando específico (somente terminável Ctrl-C) seja executado por 5 minutos automaticamente?

Por exemplo:

minute-command 5-minutes ping www.google.com

ou qualquer outro comando que não seja finalizado.

Gostaria de poder especificar um limite de tempo, não apenas 5 minutos.

eckhart
fonte

Respostas:

67

Existem (pelo menos) dois programas que fornecem essa funcionalidade:

NOME

timelimit - limitar efetivamente o tempo absoluto de execução de um processo

SINOPSE

timelimit [-pq] [-S killsig] [-s warnsig] [-T killtime] [-t warntime] command [arguments ...]

e

NOME

timeout - execute um comando com um limite de tempo

SINOPSE

timeout [OPTION] DURATION COMMAND [ARG]...
timeout [OPTION]

Eles são empacotados da seguinte maneira:

$ dlocate `which timeout timelimit`
timelimit: /usr/bin/timelimit
coreutils: /usr/bin/timeout

Comparação:

/-----------------------------+------------+----------------\
|            Feature          |  timelimit |     timeout    |
+=============================+============+================+
|   time to run               | -t time    | first argument |
+-----------------------------+------------+----------------+
|   terminate signal          | -s signal  | -s signal      |
+-----------------------------+------------+----------------+
|   grace period              | -T time    | -k time        |
+-----------------------------+------------+----------------+
|   kill signal               | -S signal  | (note 1)       |
+-----------------------------+------------+----------------+
|   propagate signals         | -p         | (note 2)       |
\-----------------------------+------------+----------------/

Notas:

  1. timeoutsempre usa SIGKILLcomo sinal de último recurso.
  2. timeout não possui nenhuma funcionalidade para sair com um sinal quando o programa filho o faz.

O status de saída dos dois programas é diferente, mas é difícil resumir bem, por isso sugiro que você consulte as páginas de manual.

Como timeouté instalado em mais sistemas por padrão ( coreutilsé um pacote padrão em muitas distribuições), sugiro que você use isso, a menos que precise da funcionalidade extra fornecida por timelimit.

Toby Speight
fonte
10
Por que você usaria um e não o outro?
Brice M. Dempsey
2
@ BriceM.Dempsey Na minha máquina local, o tempo limite está presente, mas o tempo limite não está (embora esteja disponível no repositório). Portanto, se um deles vier pré-instalado ... Além disso, apenas observando as assinaturas de método, posso ver que eles fazem coisas diferentes e têm casos de uso diferentes.
Benubird 25/02
Observe que existem pelo menos duas implementações de timelimit. Veja Como posso matar um processo e verifique se o PID não foi reutilizado para obter mais informações sobre eles e sobre o tempo limite do GNU.
sch 25/02
1
@ BriceM.Dempsey timeoutestá incluída no GNU coreutils , timelimitnão é. Você pode ter instalado nenhum, um ou ambos. No segundo, é relatado que o limite de tempo executa um comando e finaliza o processo gerado após um determinado tempo com um determinado sinal. Um sinal de “aviso” é enviado primeiro e, depois de um tempo limite, um sinal de “morte”, semelhante à maneira como init (8) opera no desligamento. Portanto, mesmo um comportamento diferente no meio .
Hastur
Como o GNU coreutils é pré-instalado em todos os GNU / Linux, timeouté o caminho a percorrer.
Rexkogitans
87

Na verdade, timeouté para isso:

TIMEOUT(1)                          User Commands                         TIMEOUT(1)

NAME
       timeout - run a command with a time limit

SYNOPSIS
       timeout [OPTION] DURATION COMMAND [ARG]...
       timeout [OPTION]

DESCRIPTION
       Start COMMAND, and kill it if still running after DURATION.

lx@lxtp:~$ dpkg -S /usr/bin/timeout
coreutils: /usr/bin/timeout
Gombai Sándor
fonte
16

Pure bashconstruído em, sem coreutils

Descobri que esta solução funciona em bashdepender de um built-in comando sem chamar um executável externo. Funciona em sistemas onde, eventualmente, nem sequer são instalados os coreutils [ 1 ]

YourCommand & read -t 300 ;  kill $!                           # 1st version
YourCommand & read -t 300 || kill $!                           # 2nd version 

Explicações : como de costume quando você enviar um comando no fundo com &, seu PID é armazenado na variável interna $!(presente na versão moderna dash, csh, bash, tcsh, zsh...).
O que realmente faz a diferença entre as conchas é a presença do built-in comando read[ 2 ] e da opção -t. Na 1ª versão, se o usuário não concluir uma linha de entrada antes da quantidade especificada de segundos, a instrução será encerrada e um código de retorno de erro será gerado.

-t TIMEOUT Faz com que a leitura atinja o tempo limite e retorne a falha se uma linha completa de entrada não for lida dentro de segundos de TIMEOUT.

A segunda versão funciona como a 1ª, mas você pode abortar o tempo limite final pressionando enter.
De fato, o operador ou ||executa a killinstrução somente se o readcomando sair com um código de retorno diferente de zero, como quando o tempo limite expirar. Se você pressionar enterantes desse momento, ele retornará 0 e não matará seu comando anterior.


Soluções Coreutils [ 1 ]

Quando coreutils estão presentes em seu sistema e você não tem nenhuma necessidade de economizar o tempo e os recursos para chamar um programa externo, timeoute sleepe são ambos excelentes maneiras para alcançar seu objetivo.

timeoutO uso de timeouté direto.
Eventualmente, você pode considerar usar também a -kopção de enviar um sinal de interrupção adicional se o primeiro falhar.

timeout 5m YourCommand                                         # 3rd version 

sleep Com sleepvocê, você pode usar sua fantasia ou inspirar-se [ 3 ] . Observe que você pode deixar seu comando em segundo plano ou em primeiro plano (por exemplo, topgeralmente precisa estar em primeiro plano).

YourCommand & sleep 5m; kill $!                                # 4th Background
YourCommand & pid=$! ; (sleep 5m; kill $pid;) &                # 5th Background

bash -c '(sleep 5m; kill $$) & exec YourCommand'               # 6th Foreground
(cmdpid=$BASHPID; (sleep 5m; kill $cmdpid) & exec YourCommand) # 7th Foreground

Explicações

  • Na quarta versão, você executa em segundo plano YourCommande, em seguida, seu shell sleepfica por 5 minuites. Quando terminar, o último processo em segundo plano ( $!) será eliminado. Você para sua concha.
  • Na quinta versão, você executa em segundo plano YourCommande armazena imediatamente esse PID na variável $pid. Em seguida, você executa em segundo plano uma soneca de 5 minutos e seu consequente comando que mata o PID armazenado. Como você enviou esse grupo de comandos em segundo plano, não interrompe seu shell. Você precisa armazenar o PID em uma variável porque o valor de $!pode ser atualizado por uma eventual execução de outro programa em segundo plano. Em palavras simples, você evita o risco de matar o processo errado ou nenhum processo.
  • Na 6ª versão, ele é chamado de novo shell bash que se suicida em 5 minutos $$, e então é executado seu comando que permanece em primeiro plano.
  • Na 7ª versão, é invocado um subshell ()que armazena seu PID em uma variável ( cmdpid) e se mata com outro subshell enviado na execução em segundo plano, depois executa YourCommand em primeiro plano.

É claro que em cada versão você pode enviar o sinal de interrupção necessário, do padrão ao extremo kill -9 , para ser usado apenas quando realmente necessário.

Referências

  • [ 1 ] Os Coreutils
  • [ 2 ] Guia para iniciantes do Bash
  • [ 3 ] O BashFAQ
Hastur
fonte
2
"Com sono, você pode usar a sua fantasia ou tomar algumas inspirações [link para Bash FAQ]" +1 #thatsenoughinternetfortoday
joeytwiddle
11

Para o seu caso particular, a maioria das implementações de pingsuporte -cou --countpara finalizar após um número específico de pings:

ping -c 300 host.example.com

Para uma solução mais geral, consulte as outras respostas.

Toby Speight
fonte
7
Tenho a impressão de que o OP apenas forneceu pingum exemplo concreto. Ele não quer uma solução caso a caso para cada programa diferente.
User1717828
8

Você pode fazer isso com um script simples como este:

#!/bin/bash

#
# $1 is the time to let the program run, and $2, $3, ... are the command itself.
#

"${@:2}" &
PID=$!
sleep "${@:1:1}"
kill -2 $PID

(sinal SIGINT = 2 usado conforme sugestão de Matija Nalis no comentário abaixo).

Uma explicação da expressão bash (incomum?) $@:...: Os parâmetros posicionais, ( $*, $@, "$*", "$@") admitem a seguinte especificação extra, por exemplo:

"${@:START:COUNT}"

o que significa: de todos os parâmetros, pegue COUNT deles, sendo o primeiro deles o da posição STARTth; se COUNT for omitido, leve todos eles até o final, começando com a posição STARTth. Lembre-se de que $0é o nome do programa. Se START for negativo, comece a contar a partir do final e lembre-se de que COUNT não pode ser negativo; portanto, o último argumento é "${@:-1}". Além disso, sempre inclua os parâmetros posicionais entre aspas duplas.

MariusMatutiae
fonte
1
Eu provavelmente projetaria o script para levar o tempo como o primeiro ou o último argumento e executar todos os outros argumentos como o comando. Deixar o bash dividir palavras $1é bastante nojento. Além disso, você poderia apenas sleep "$1"ou algo assim countdown.
Peter Cordes
1
@ PeterCordes Feito, conforme seus desejos.
MariusMatutiae
O tempo no primeiro argumento permite time=$1; shifte, em seguida, você não precisa da sintaxe de divisão da matriz para os parâmetros posicionais. Mas sim, isso é muito mais simples.
Peter Cordes
1
@ PeterCordes concordou, mas você me pediu para ver se eu conhecia uma maneira mais segura de fazer isso e eu tinha acabado de ser preguiçoso ou simplesmente ignorante. ri muito.
MariusMatutiae
como o pôster perguntou especificamente sobre o programa que pode ser interrompido ctrl-c, provavelmente é melhor tentar em kill -INTvez de kill -9(ou pelo menos tentar -9apenas depois de alguns segundos se o primeiro não foi bem-sucedido - isso dá ao programa a chance de sair de forma limpa e não deixar arquivos temporários etc)
Matija Nalis
5

Desde que você mencionou pingno seu exemplo; este comando tem algumas opções para parar após um período específico de tempo:

  • w deadline Especifique um tempo limite, em segundos, antes da saída do ping, independentemente de quantos pacotes foram enviados ou recebidos. Nesse caso, o ping não para após o envio do pacote de contagem , aguarda o vencimento do prazo final ou até que as sondas de contagem sejam respondidas ou por alguma notificação de erro da rede.
  • W timeout Tempo para aguardar uma resposta, em segundos. A opção afeta apenas o tempo limite na ausência de respostas, caso contrário, o ping aguarda dois RTTs.

-man ping

user2428118
fonte