Kill program po tym, jak wypisze dany wiersz ze skryptu powłoki


16

Tło:

Piszę skrypt testowy dla oprogramowania biologii obliczeniowej. Oprogramowanie, które testuję, może trwać kilka dni, a nawet tygodni, więc ma wbudowane funkcje odzyskiwania w przypadku awarii systemu lub awarii zasilania.

Próbuję wymyślić, jak przetestować system odzyskiwania. W szczególności nie mogę znaleźć sposobu na „zawieszenie” programu w kontrolowany sposób. Zastanawiałem się nad czasem, aby rozkaz SIGKILL działał po pewnym czasie. Prawdopodobnie nie jest to idealne rozwiązanie, ponieważ nie gwarantuje się, że przypadek testowy będzie działał z tą samą prędkością za każdym razem (działa we wspólnym środowisku), więc porównanie dzienników z pożądanymi danymi wyjściowymi byłoby trudne.

To oprogramowanie drukuje wiersz dla każdej sekcji analizy, którą wykonuje.

Pytanie:

Zastanawiałem się, czy istnieje dobry / elegancki sposób (w skrypcie powłoki), aby przechwycić dane wyjściowe z programu, a następnie zabić program, gdy dany wiersz / liczba wierszy jest wysyłana przez program?


1
Dlaczego musisz zatrzymać program w określonym czasie? Z powodu buforowania danych wyjściowych możesz zabić program długo po tym, jak wypisuje sprawdzany tekst.
stardt

@stardt Chciałem zatrzymać program w określonym czasie, aby wyniki testów były bardziej kontrolowane i odtwarzalne. Znalazłem sposób na obejście tego teraz. Zabiję program tylko raz ręcznie, a następnie przetestuję tylko kod odzyskiwania. Wszystkie próby odzyskiwania rozpoczną się od tego samego punktu. Testuję, w jaki sposób się odzyskuje, a nie w końcu awarie :)
Paul

Odpowiedzi:


7

Skrypt otoki taki jak ten jest standardowym podejściem. Skrypt uruchamia program w tle, a następnie zapętla, sprawdzając plik dziennika co minutę pod kątem jakiegoś ciągu. Jeśli ciąg zostanie znaleziony, program w tle zostanie zabity i skrypt zostanie zamknięty.

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

15

Jeśli twój program zakończy działanie na SIGPIPE (co jest domyślną akcją), powinno wystarczyć przekierowanie wyjścia do czytnika, który wyjdzie po przeczytaniu tego wiersza.

Może to być tak proste jak

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

Jeśli chcesz ukryć domyślne wyjście, użyj

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

Podobnie, jeśli chce się zatrzymać po określonej liczbie linii, można użyć głowy zamiast sed, np

$ program | head -n$NUMBER_OF_LINES_TO_STOP_AFTER

Dokładny czas, w którym nastąpi zabójstwo , zależy od buforowania zachowania terminala, jak sugeruje stardt w komentarzach.


-1. Tutaj jest poważna wada. Program zakończy się tylko wtedy, gdy (jeśli) spróbuje zapisać do (zepsutego) potoku po sedzakończeniu. OP mówi: „To oprogramowanie drukuje wiersz dla każdej sekcji analizy, którą wykonuje”. Może to oznaczać, że program ukończy jedną dodatkową sekcję! Dowód koncepcji: { echo 1; sleep 3; >&2 echo peekaboo; sleep 3; echo 2; } | sed -e '/1/q'. Zobacz tę odpowiedź . Możliwe obejście: ( program & ) | sed -e '/Suitable text from the line/q'; killall program. Uczyń swoją odpowiedź świadomą wadę, a ja odwołam moje zdanie.
Kamil Maciorowski

Hmmm ... rzeczywiście. Problem buforowania sprawia, że ​​jest niewiarygodny nawet po wprowadzeniu ulepszeń (choć oczywiście wpływa to na każde rozwiązanie, które tu widzę). Lubię, gdy jest to możliwe, korzystać z infrastruktury sygnałowej, ale w pewnym momencie zaczyna tracić swoją elegancję. Być może lepiej po prostu zeskrobać całość.
dmckee --- były moderator kociak

7

Alternatywą dla odpowiedzi dmckee jest także greppolecenie z -mopcją (patrz np. Ta strona podręcznika ):

compbio | grep -m 1 "Text to match"

zatrzymać, gdy zostanie znaleziony 1 wiersz pasujący do tekstu lub

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

czekać na 10 wierszy, które nie pasują do podanego tekstu.


3

Możesz użyć expect(1)do obejrzenia standardowego programu, a następnie wykonać predefiniowane działanie na niektórych wzorcach.

#!/usr/bin/env expect

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

Lub linijka do osadzenia w innym skrypcie:

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

Pamiętaj, że expectpolecenie nie jest domyślnie dostarczane z każdym systemem operacyjnym. Może być konieczne jego zainstalowanie.


To miłe rozwiązanie. Polecam wspomnieć set timeout -1o ustawieniu limitu czasu nieokreślonego, w przeciwnym razie wszystko zamknie się poniżej expectdomyślnego limitu 10 sekund.
pepper_chico

1

Możesz użyć instrukcji „while”:

Musisz przesłać dane wyjściowe swojego oprogramowania (lub jego dzienników), a następnie, dla każdej nowej linii, wykonać test warunkowy, a następnie uruchomić polecenie kill -KILL. Na przykład (testowałem z plikiem / var / log / messages jako plikiem dziennika):

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

Lub bezpośrednio z oprogramowaniem:

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

Musisz zastąpić ścieżkę dziennika / nazwę aplikacji, pasujący wiersz i PID do zabicia (możesz także użyć killall).

Powodzenia w oprogramowaniu,

Hugo,

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.