Qual è un modo semplice per consentire l'esecuzione di un comando per 5 minuti? [duplicare]


66

Questa domanda ha già una risposta qui:

Esiste un modo semplice Ctrl-Cper eseguire automaticamente un comando specifico (solo terminabile ) per 5 minuti?

Per esempio:

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

o qualsiasi altro comando che non si interrompa da solo.

Vorrei poter specificare un limite di tempo, non solo 5 minuti.

Risposte:


67

Esistono (almeno) due programmi che forniscono questa funzionalità:

NOME

timelimit - limitare efficacemente i tempi assoluti di esecuzione di un processo

SINOSSI

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

e

NOME

timeout - eseguire un comando con un limite di tempo

SINOSSI

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

Sono confezionati come segue:

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

Confronto:

/-----------------------------+------------+----------------\
|            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)       |
\-----------------------------+------------+----------------/

Appunti:

  1. timeoutusa sempre SIGKILLcome segnale dell'ultima risorsa.
  2. timeout non ha alcuna funzionalità per uscire con un segnale quando il programma figlio lo fa.

Lo stato di uscita dei due programmi differisce, ma è difficile riassumerlo in modo accurato, quindi ti consiglio di consultare tu stesso le pagine del manuale.

Poiché timeoutè installato su più sistemi di default ( coreutilsè un pacchetto standard in molte distribuzioni), ti suggerisco di usarlo a meno che tu non abbia bisogno delle funzionalità extra fornite da timelimit.


10
Perché dovresti usare l'uno anziché l'altro?
Brice M. Dempsey,

2
@ BriceM.Dempsey Sul mio computer locale, il timeout è presente, ma il timelimit no (sebbene sia disponibile dal repository). Quindi, se uno di essi viene preinstallato ... A parte questo, solo guardando le firme del metodo posso vedere che fanno cose diverse e hanno casi d'uso diversi.
Benubird,

Si noti che esistono almeno due implementazioni di timelimit. Vedi Come posso interrompere un processo ed essere sicuro che il PID non sia stato riutilizzato per ulteriori informazioni su di essi e timeout GNU.
sch

1
@ BriceM.Dempsey timeoutè incluso nei coreutils GNU , timelimitnon lo è. Non puoi averne installato nessuno, uno o entrambi. Per il secondo viene riportato timelimit esegue un comando e termina il processo generato dopo un determinato tempo con un determinato segnale. Prima viene inviato un segnale di "avvertimento" , quindi, dopo un timeout, un segnale di "interruzione", simile al modo in cui init (8) opera allo spegnimento. Quindi anche un comportamento diverso nel mezzo .
Hastur,

Poiché GNU coreutils è preinstallato su ogni GNU / Linux, timeoutè la strada da percorrere.
rexkogitans,

87

In realtà, questo è ciò che timeoutserve per:

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

16

Integrato puro bash, senza coreutils

Ho scoperto che questa soluzione funziona bashbasandosi su un comando integrato senza chiamare un eseguibile esterno. Funziona su un sistema dove alla fine non sono stati nemmeno installati i coreutils [ 1 ]

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

Spiegazioni : come al solito quando si invia un comando in background con &, il suo PID è memorizzato nella variabile interna $!(presente nella versione moderna di dash, csh, bash, tcsh, zsh...).
Ciò che fa davvero la differenza tra le shell è la presenza del comando integrato read[ 2 ] e dell'opzione -t. Nella prima versione se l'utente non completerà una riga di input prima del numero di secondi specificato, l'istruzione verrà chiusa e verrà generato un codice di ritorno dell'errore.

-t TIMEOUT Consente di leggere il timeout e di restituire l'errore se non viene letta una riga di input completa entro TIMEOUT secondi.

La seconda versione funziona come la prima ma è possibile interrompere il timeout dell'uccisione semplicemente premendo enter.
In effetti l'operatore o ||esegue l' killistruzione solo se il readcomando esce con un codice di ritorno diverso da zero, come quando il timeout è scaduto. Se premi enterprima di quel momento, restituirà 0 e non ucciderà il tuo comando precedente.


Coreutils solutions [ 1 ]

Quando coreutils sono presenti sul sistema e non avete bisogno di risparmiare il tempo e le risorse per chiamare un programma esterno, timeoute sleepe sono entrambi i modi perfetti per raggiungere il tuo obiettivo.

timeoutL'uso di timeoutè semplice.
Alla fine puoi considerare di usare anche l' -kopzione per inviare un segnale di kill aggiuntivo se il primo fallisce.

timeout 5m YourCommand                                         # 3rd version 

sleep Con sleepte puoi usare la tua fantasia o prendere qualche ispirazione [ 3 ] . Nota che puoi lasciare il tuo comando in background o in primo piano (ad esempio, di topsolito deve essere in primo piano).

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

spiegazioni

  • Nella quarta versione esegui in background, YourCommandquindi la tua shell sleeps per 5 minuiti. Al termine, l'ultimo processo in background ( $!) verrà interrotto. Smetti di sgusciare.
  • Nella quinta versione invece si esegue in background YourCommande si memorizza immediatamente quel PID nella variabile $pid. Quindi esegui in background un pisolino di 5 minuti e il conseguente comando che ucciderà quel PID memorizzato. Poiché hai inviato questo gruppo di comandi in background, non fermi la shell. È necessario archiviare il PID in una variabile perché il valore di $!può essere aggiornato da un'eventuale esecuzione di un altro programma in background. In parole semplici, si evita il rischio di uccidere il processo sbagliato o nessun processo.
  • Nella sesta versione si chiama una nuova shell bash che si suiciderà in 5 minuti tramite $$, quindi viene eseguito il comando che rimane in primo piano.
  • Nella 7a versione viene invocata una subshell ()che memorizza il suo PID in una variabile ( cmdpid) e si uccide con un'altra subshell inviata nell'esecuzione in background, quindi esegue YourCommand in primo piano.

Ovviamente in ogni versione è possibile inviare il segnale di interruzione necessario, da quello predefinito all'estremo kill -9 , da utilizzare solo quando realmente necessario.

Riferimenti

  • [ 1 ] The Coreutils
  • [ 2 ] La guida per principianti di Bash
  • [ 3 ] BashFAQ

2
"Con il sonno puoi usare la tua fantasia o prendere qualche ispirazione [link alle FAQ di Bash]" +1 #thatsenoughinternetfortoday
joeytwiddle

11

Per il tuo caso particolare, la maggior parte delle implementazioni di pingsupporto -co --countper terminare dopo un determinato numero di ping:

ping -c 300 host.example.com

Per una soluzione più generale, vedere le altre risposte.


7
Ho l'impressione che OP abbia appena fornito pingun esempio concreto. Non vuole una soluzione caso per caso per ogni programma diverso.
user1717828

8

Puoi farlo con un semplice script come questo:

#!/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

(segnale SIGINT = 2 usato secondo il suggerimento di Matija Nalis nel commento qui sotto).

Una spiegazione dell'espressione bash (non comune?) $@:...: I parametri posizionali, ( $*, $@, "$*", "$@") ammettono la seguente specifica aggiuntiva, per esempio:

"${@:START:COUNT}"

il che significa: di tutti i parametri, prendine COUNT, il primo ad essere preso è quello nella posizione START; se COUNT viene omesso, prendili tutti fino alla fine, iniziando dalla posizione STARTth. Ricorda che $0è il nome del programma. Se START è negativo, inizia il conteggio dalla fine e ricorda che COUNT non può essere negativo, quindi l'ultimo argomento è "${@:-1}". Inoltre, includi quasi sempre i parametri posizionali tra virgolette doppie.


1
Probabilmente progetterei lo script in modo che prendesse il tempo come primo o ultimo argomento, ed eseguissi tutti gli altri argomenti come comando. Lasciare bash dividere le parole $1è piuttosto disgustoso. Inoltre, potresti semplicemente sleep "$1"o qualcosa dentro countdown.
Peter Cordes,

1
@PeterCordes Fatto, secondo i tuoi desideri.
MariusMatutiae,

Il tempo nel primo argomento ti consente di farlo time=$1; shift, e quindi non è necessaria la sintassi di suddivisione in array per i parametri posizionali. Ma sì, è molto più semplice.
Peter Cordes,

1
@PeterCordes D'accordo, ma mi hai chiesto di vedere se conoscevo un modo più sicuro per farlo ed ero appena stato pigro o se ero semplicemente ignorante. lol.
MariusMatutiae,

come il poster ha specificamente chiesto del programma che può essere interrotto ctrl-c, è probabilmente meglio provare kill -INTinvece di kill -9(o almeno provare -9solo dopo pochi secondi se il primo non ha avuto successo - dà la possibilità al programma di uscire in modo pulito e non lasciare file temporanei ecc.)
Matija Nalis,

5

Dal momento che menzioni pingnel tuo esempio; questo comando ha alcune opzioni per fermarsi dopo un determinato periodo di tempo:

  • w deadline Specificare un timeout, in secondi, prima della chiusura del ping, indipendentemente dal numero di pacchetti inviati o ricevuti. In questo caso il ping non si interrompe dopo l' invio del pacchetto di conteggio , attende la scadenza del termine o fino alla risposta delle sonde di conteggio o alla notifica di errori dalla rete.
  • W timeout Tempo di attesa per una risposta, in secondi. L'opzione influisce solo sul timeout in assenza di risposte, altrimenti il ping attende due RTT.

-man ping

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.