Uccidi il programma dopo aver emesso una determinata riga, da uno script di shell


15

Sfondo:

Sto scrivendo un test script per un pezzo di software di biologia computazionale. Il software che sto testando può richiedere giorni o addirittura settimane per essere eseguito, quindi ha una funzionalità di ripristino integrata, in caso di crash del sistema o interruzioni di corrente.

Sto cercando di capire come testare il sistema di recupero. In particolare, non riesco a capire un modo per "bloccare" il programma in modo controllato. Stavo pensando di programmare in qualche modo un'istruzione SIGKILL da eseguire dopo un certo periodo di tempo. Questo probabilmente non è l'ideale, in quanto il test case non è garantito per funzionare sempre alla stessa velocità (funziona in un ambiente condiviso), quindi sarebbe difficile confrontare i log con l'output desiderato.

Questo software stampa una riga per ogni sezione di analisi che completa.

Domanda:

Mi chiedevo se ci fosse un modo buono / elegante (in uno script di shell) per catturare l'output di un programma e quindi uccidere il programma quando una determinata linea / # di linee viene emessa dal programma?


1
Perché è necessario interrompere il programma in un momento specifico? A causa del buffering dell'output potresti finire per uccidere il programma molto tempo dopo aver emesso il testo che stai cercando.
inizio

@stardt Volevo interrompere il programma in un momento specifico in modo che i risultati dei test fossero più controllati e riproducibili. Ho trovato un modo per aggirare questo adesso. Ucciderò il programma solo una volta manualmente, quindi testerò solo il codice di recupero. Tutti i tentativi di recupero inizieranno dallo stesso punto. Sto testando come si recupera, non come si blocca dopo tutto :)
Paul

Risposte:


7

Uno script wrapper come questo è un approccio standard. Lo script esegue il programma in background e quindi esegue il ciclo, controllando ogni minuto il file di registro per individuare una stringa. Se la stringa viene trovata, il programma in background viene ucciso e lo script viene chiuso.

command="prog -foo -whatever"
log="prog.log"
match="this is the what i want to match"

$command > "$log" 2>&1 &
pid=$!

while sleep 60
do
    if fgrep --quiet "$match" "$log"
    then
        kill $pid
        exit 0
    fi
done

14

Se il programma terminerà su SIGPIPE (che è l'azione predefinita), dovrebbe essere sufficiente reindirizzare l'output in un lettore che uscirà leggendo quella riga.

Quindi potrebbe essere semplice come

$ program | sed -e '/Suitable text from the line/q'

Se si desidera eliminare l'output predefinito, utilizzare

$ program | sed -n -e '/Suitable text from the line/q'

Allo stesso modo se si vuole fermarsi dopo un certo numero di linee si potrebbe usare la testa al posto di sed, ad es

$ program | head -n$NUMBER_OF_LINES_TO_STOP_AFTER

Il tempo esatto di a cui l'uccisione avviene non dipende dal comportamento buffer del terminale come stardt suggerisce nei commenti.


-1. C'è un grave difetto qui. Il programma terminerà solo quando (se) tenterà di scrivere sulla pipe (rotta) dopo aver sedterminato. OP dice "Questo software stampa una riga per ogni sezione di analisi che completa". Può significare che il programma completerà una sezione extra! Proof of concept: { echo 1; sleep 3; >&2 echo peekaboo; sleep 3; echo 2; } | sed -e '/1/q'. Vedere questa risposta . Soluzione possibile: ( program & ) | sed -e '/Suitable text from the line/q'; killall program. Rendi consapevole la tua risposta al difetto e io revocerò il mio voto negativo.
Kamil Maciorowski il

Hmmm ... davvero. E il problema del buffering lo rende inaffidabile anche con i tuoi miglioramenti (anche se, ovviamente, ciò influisce su ogni soluzione che vedo qui). Mi piace fare uso dell'infrastruttura del segnale quando possibile, ma a un certo punto inizia a perdere la sua eleganza. Forse è meglio solo scartare il tutto.
dmckee --- ex gattino moderatore

7

In alternativa alla risposta di dmckee, è possibile utilizzare anche il grepcomando con l' -mopzione (vedi ad esempio questa pagina man ):

compbio | grep -m 1 "Text to match"

per interrompere quando viene trovata 1 riga corrispondente al testo o

compbio | grep -v -m 10 "Text to match"

per attendere 10 righe che non corrispondono al testo indicato.


3

È possibile utilizzare expect(1)per guardare lo stdout di un programma, quindi eseguire azioni predefinite su alcuni schemi.

#!/usr/bin/env expect

spawn your-program -foo -bar 
expect "I want this text" { close }

O un one-liner da incorporare in un altro copione:

expect -c "spawn your-program -foo -bar; expect \"I want this text\" { close }"

Si noti che il expectcomando non viene fornito con tutti i sistemi operativi per impostazione predefinita. Potrebbe essere necessario installarlo.


Questa è una bella soluzione. Consiglio di menzionare set timeout -1di impostare il timeout indefinito, altrimenti si chiuderà tutto in expecttimeout predefinito di 10 secondi.
pepper_chico,

1

È possibile utilizzare un'istruzione "while":

È necessario reindirizzare l'output del software (o dei relativi registri) e quindi, per ogni nuova riga, eseguire un test condizionale, quindi eseguire kill -KILL. Ad esempio (ho provato con / var / log / messages come file di registro):

tail -f /var/log/messages | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

O direttamente con il software:

./biosoftware | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

È necessario sostituire il percorso del registro / il nome dell'applicazione, la riga da abbinare e il PID da eliminare (è anche possibile utilizzare killall).

Buona fortuna con il tuo software,

Hugo,

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.