Terminare un ciclo infinito


52

Ho un comando che voglio eseguire di nuovo automaticamente ogni volta che termina, quindi ho eseguito qualcosa del genere:

while [ 1 ]; do COMMAND; done;

ma se non riesco a fermare il ciclo con Ctrl-cquello che uccide COMMANDe non l'intero ciclo.

Come potrei ottenere qualcosa di simile ma che posso fermare senza dover chiudere il terminale?


11
Se sono in bash, uso Ctrl-Z per interrompere il lavoro e quindi "uccidi% 1" per ucciderlo.
Paul Cager,

3
Aspetta ... Linus è stato citato dicendo: "Sappiamo tutti che Linux è fantastico ... fa cicli infiniti in 5 secondi." - Quindi davvero ... aspetta solo qualche secondo in più, dovrebbe completarsi.
Lornix,

@PaulCager ha funzionato anche per me! Perché funziona dove Ctrl-C non funziona?
Ciro Santilli 15 改造 中心 法轮功 六四 事件

@cirosantilli uccide il lavoro esterno (il "wrapper" bash). In alcune situazioni, non ucciderà immediatamente il "COMANDO", ad esempio, se lo fai da sfondo, potrebbe passare di soppiatto vivo anche se il suo genitore è morto. Ma il ciclo è morto, e questa è la parte importante.
Orion,

Risposte:


38

Controlla lo stato di uscita del comando. Se il comando è stato terminato da un segnale, il codice di uscita sarà 128 + il numero del segnale. Dalla documentazione online GNU per bash :

Ai fini della shell, un comando che esce con uno stato di uscita pari a zero ha avuto esito positivo. Uno stato di uscita diverso da zero indica un errore. Questo schema apparentemente controintuitivo viene utilizzato, quindi esiste un modo ben definito per indicare il successo e una varietà di modi per indicare varie modalità di fallimento. Quando un comando termina con un segnale fatale il cui numero è N, Bash utilizza il valore 128 + N come stato di uscita.

POSIX specifica inoltre che il valore di un comando che termina con un segnale è maggiore di 128, ma non sembra specificare il suo valore esatto come GNU:

Lo stato di uscita di un comando che è terminato perché ha ricevuto un segnale deve essere riportato come maggiore di 128.

Ad esempio se si interrompe un comando con control-C il codice di uscita sarà 130, poiché SIGINT è il segnale 2 sui sistemi Unix. Così:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done

9
Va detto che questo non è garantito, infatti molte applicazioni non lo faranno.
Chris Down

1
@Kyle Jones: puoi collegarti ai documenti POSIX / GNU che lo menzionano?
Ciro Santilli 15 改造 中心 法轮功 六四 事件

@cirosantilli Done.
Kyle Jones,

@KyleJones grazie! Ancora in pratica non funziona per COMMAND = paplay alert.ogg, forse perché paplaygestisce il segnale?
Ciro Santilli 16 改造 中心 法轮功 六四 事件

@cirosantilli Sì, questo è il motivo. Se un processo gestisce il segnale ed esce, è diverso dal processo che viene terminato da un segnale non gestito.
Kyle Jones,

49

Puoi interrompere e mettere il tuo lavoro in background mentre è in esecuzione utilizzando ctrl+ z. Quindi puoi uccidere il tuo lavoro con:

$ kill %1

Dove [1] è il tuo numero di lavoro.


Vedi anche questa risposta per spiegazioni e altro.
Skippy le Grand Gourou,

2
Questa risposta relativamente recente funziona semplicemente. Deve essere votato. +1
shivams,

Mi hai aiutato molto. Questo è quello che ho cercato in questa domanda :)
Maximilian Ruta,

18

Direi che potrebbe essere meglio inserire il tuo loop infinito in uno script e gestire i segnali lì. Ecco un punto di partenza di base . Sono sicuro che vorrai modificarlo per adattarlo. Lo script usa trapper catturare ctrl- c(o SIGTERM), uccide il comando (l'ho usato sleepqui come test) ed esce.

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done

4
Bello. Ecco come ho usato questo suggerimento per creare un wrapper netcat con avvio automatico:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas

2
se aggiungi questo trapapproccio allo stesso script (bash) con il ciclo infinito da uccidere, usa $$invece di $!(vedi qui )
ardnew,


8

Se esegui bash con -eesso uscirà in caso di condizioni di errore:

#!/bin/bash -e
false # returns 1
echo This won't be printed

8

Perché non semplicemente,

while [ 1 ]; do COMMAND || break; done;

O quando usato in uno script,

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;

1
Soluzione molto elegante. Ma questo non funzionerebbe solo se COMMAND restituisce sempre uno stato di uscita riuscito?
Howardh

Sì @howardh, è corretto.
Dale Anderson,

3

Preferisco un'altra soluzione:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

Per terminare il loop, fai semplicemente:

rm .runcmd && kill `pidof COMMAND`

2
  1. Puoi sempre terminare un processo usando il suo PID, non è necessario chiudere il tuo terminale
  2. Se vuoi eseguire qualcosa in un ciclo infinito come un demone, ti conviene metterlo in secondo piano
  3. while : creerà un ciclo infinito e ti salva scrivendo il [ 1 ]

    while :; do COMMAND; done &

Questo stamperà il PID. Se si esce dal prompt utilizzando, ctrl+dil processo in background non verrà chiuso e in seguito è possibile terminare il processo da qualsiasi luogokill PID

Se perdi traccia del tuo PID, puoi utilizzare pstree -pa $USERo pgrep -fl '.*PROCESS.*'per aiutarti a trovarlo


0

Usa trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
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.