Zabicie skryptu powłoki działającego w tle


12

Napisałem skrypt powłoki do monitorowania katalogu za pomocą narzędzia inotifywait programu inotifyt-tools. Chcę, aby ten skrypt działał nieprzerwanie w tle, ale chcę też móc go zatrzymać w razie potrzeby.

Aby działał nieprzerwanie, użyłem while true; lubię to:

while true;
do #a set of commands that use the inotifywait utility
end

Zapisałem go w pliku /bini sprawiłem, że jest wykonywalny. Aby działał w tle, użyłem nohup <script-name> &i zamknąłem terminal.

Nie wiem jak zatrzymać ten skrypt. Przejrzałem tutaj odpowiedzi i bardzo ściśle powiązane pytanie tutaj .

AKTUALIZACJA 1: Na podstawie odpowiedzi @InfectedRoot poniżej udało mi się rozwiązać problem przy użyciu następującej strategii. Pierwsze użycie

ps -aux | grep script_name

i użyj, sudo kill -9 <pid>aby zabić procesy. Potem musiałem pgrep inotifywaiti użyłem sudo kill -9 <pid>ponownie do zwrotu identyfikatora.

To działa, ale myślę, że to niechlujne podejście. Szukam lepszej odpowiedzi.

AKTUALIZACJA 2: Odpowiedź polega na zabiciu 2 procesów . Jest to ważne, ponieważ uruchomienie skryptu w wierszu polecenia inicjuje 2 procesy, 1 sam skrypt i 2 proces inotify .



2
Usunięcie -9opcji killi użycie po prostu killusunie z niej bałagan.
Sree,

1
Brawo. Aby ponownie wprowadzić trochę „$$” do kasy pieniężnej, chciałbym podkreślić, że tutaj -9opcja byłaby całkowita kill. : P
składnia błąd

Dla kompletności: istnieje czyste podejście do zabijania obu procesów naraz: Zamiast zabijać procesy osobno, możesz zabić grupę procesów skryptu, aby zabić je jednocześnie. Pgid jest taka sama jak PID głównego procesu skryptu powłoki i killto ty prefiks pgid z minusem: kill -- -<pgid>, kill -9 -<pgid>itd
cg909

Odpowiedzi:


6

Aby ulepszyć, użyć killall, a także połączyć polecenia:

ps -aux | grep script_name
killall script_name inotifywait

Lub rób wszystko w jednym wierszu:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait

Powyższe rozwiązanie działa, ale nie jest to polecenie jednowierszowe. Czy możesz to sprawdzić?
Pojawia

@ light94 Error: no processpowinno oznaczać, że już go zabiłeś. Możesz to przetestować, otwierając dwa inne programy, takie jak VLC i Geany , i wypróbowując go razem z nimi.
cremefraiche

Hej @ cremefraiche, ponieważ Earler powiedział, że twój kod działa .. ale szukałem alternatywnych rozwiązań i najczęstszym rozwiązaniem, jakie widzę, jest użycie kill <pid>. Ponieważ mój skrypt nie kończy się w ten sposób, trochę się martwię, czy mój kod jest niewłaściwy? Czy możesz mnie poprowadzić?
light94

Możesz zaoszczędzić grep -v grep, zamieniając się grep script_namew grep -e "[s]cript_name".
nmichaels,

3

Wyświetl listę zadań w tle za pomocą

# jobs

Następnie wybierz poprzednią liczbę zadań i uruchom

przykład

# fg 1 

wprowadź opis zdjęcia tutaj

przyniesie na pierwszy plan.

Następnie zabij go za pomocą CTRL + C lub łatwiejszego sposobu znalezienia PID skryptu za pomocą

ps -aux | grep script_name

wprowadź opis zdjęcia tutaj

Następnie zabij za pomocą pid

sudo kill -9 pid_number_here

2
Pierwsza opcja nie jest poprawna w moim przypadku, ponieważ zamykam powłokę po napisaniu skryptu i myślę, że polecenie zadań działa tylko dla tej samej powłoki. Wypróbowałem też drugą opcję i to zabija proces, ale kiedy robię pgrep inotifywait, nadal widzę go jako proces.
light94

Zadania podadzą listę zadań nawet po zamknięciu powłoki. Proces dobiegnie końca, będzie tam.
Babin Lonston,

2
Próbowałem, ale nie zrobiłem tego.
light94

@ light94 Masz rację. Często zdarzało się tutaj, że jobspolecenie naprawdę wyświetlało uruchomione zadania tylko w powłoce. Kiedy zamknąłem pewne okno terminala, jedynym sposobem na uzyskanie do nich dostępu było wejście ps(patrz powyżej). Na pewno nie wymyślisz tego.
składnia błąd

2

Możesz użyć ps+ greplub, pgrepaby uzyskać nazwę procesu / pid ; później użyj killall/, pkillaby zabić nazwę procesu lub użyj, killaby zabić pid. Wszystkie poniższe powinny zadziałać.

killall $(ps aux | grep script_name | grep -v grep | awk '{ print $1 }') && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $1 }' | xargs killall) && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $2 }' | xargs kill) && killall inotifywait
(pgrep -x script_name | xargs kill) && pkill -x inotifywait
pkill -x script_name && pkill -x inotifywait

Najważniejsze jest to, że powinieneś upewnić się, że zabijasz tylko te procesy, które chcesz zabić.

pkill/ pgreppasuje do wzorca zamiast do dokładnej nazwy , więc jest bardziej niebezpieczny; tutaj -xdodaje się, aby dopasować dokładną nazwę.

Ponadto, gdy używasz pgrep/ pkillmożesz potrzebować

  • -faby dopasować pełny wiersz poleceń (podobnie jak ps auxrobi)
  • -a aby wydrukować także nazwę procesu.

Po wykonaniu powyższego pojawia się błąd użytkowania w każdym z nich. kopiuję wklejanie i zastępowanie skryptu. Dostaję użycie zabicia jako wyjścia
light94

Czy nazwa skryptu jest uruchomiona? Działa dla mnie (debian jessie)
Hongxu Chen,

tak, to działa. a twoje rozwiązanie jest prawie takie samo jak rozwiązania @cremefraiche powyżej, więc spodziewałem się, że zadziała w ten sam sposób: /
light94

Tak, podsumowałem trochę możliwych sposobów, szczególnie dodając pgrep/ pkill. Jeśli to nadal nie działa, możesz spróbować pgrepbez opcji -x. Zasadniczo nie sądzę, że dobrze jest zabijać proces w ramach jednego wiersza polecenia bez sprawdzania, czy inne procesy są dopasowane.
Hongxu Chen,

1

Jak zapewne można stwierdzić, istnieje wiele sposobów na zrobienie tego.

Jeśli chodzi o twoją „AKTUALIZACJĘ # 2” - ogólnie mówiąc, zakończenie dowolnego procesu w hierarchii rodzic-dziecko spowoduje na ogół zakończenie wszystkich powiązanych procesów. Ale istnieje wiele wyjątków od tego. Najlepiej, jeśli chcesz zakończyć ostatnie „dziecko” w drzewie procesów, a następnie rodzice (rodzice) tego dziecka powinni wyjść, jeśli nie mają innych zadań do uruchomienia. Ale jeśli zabijesz rodzica, sygnał powinien zostać przekazany dzieciom, gdy rodzic umiera, a dzieci powinny również wyjść - ale są przypadki, w których procesy potomne mogą zignorować sygnał (za pośrednictwem pułapek lub podobnych mechanizmów) i mogą kontynuować uruchomienie zostanie odziedziczone przez proces „init” (lub podobny). Ale ten temat zachowania procesu może się skomplikować i po prostu zostawię go tam ...

Jedną z metod, które lubię, jeśli nie chcę używać skryptu sterującego (opisanego poniżej), jest użycie narzędzia „screen” do uruchomienia i zarządzania procesem. Komenda „screen” jest pełna funkcji i opanowanie jej może trochę potrwać. Zachęcam do przeczytania strony podręcznika „screen”, aby uzyskać pełne wyjaśnienie. Szybki przykład uruchomienia procesu w tle to polecenie:

screen -d -m / ścieżka / do / program

To rozpocznie „/ path / to / program” w sesji „screen”.

Możesz zobaczyć swoją uruchomioną sesję za pomocą polecenia:

screen -ls

W każdej chwili możesz ponownie połączyć się z uruchomionym programem za pomocą polecenia:

screen -r

A następnie po prostu zakończ go ^ C lub czymkolwiek.

Oprócz piękna, jakim jest możliwość ponownego połączenia i rozłączenia się z procesem do woli, „ekran” przechwytuje wszystkie standardowe () stdout (), które może wytworzyć Twój program.


Ale moje osobiste preferencje w tych sprawach to mieć program kontrolny, który zarządza uruchamianiem i zatrzymywaniem procesu. Może to być nieco skomplikowane i wymaga pewnie skomplikowanych skryptów. I jak każdy skrypt, istnieją dziesiątki dobrych sposobów na zrobienie tego. Podałem przykładowy przykład metody, której rutynowo używam do uruchamiania i zatrzymywania aplikacji. Jeśli twoje zadanie jest proste, możesz wstawić je bezpośrednio do skryptu sterującego - lub możesz wywoływać ten skrypt sterujący w innym programie zewnętrznym. Należy zauważyć, że ten przykład nie jest kompleksowy pod względem zarządzania procesem. Pominąłem możliwość takich scenariuszy, jak: upewnienie się, że skrypt nie jest już uruchomiony, gdy korzystasz z opcji „start”, sprawdzanie, czy działający PID jest tak naprawdę procesem, który rozpocząłeś (np. Twój skrypt nie „ t zmarł, a inny proces został uruchomiony przy użyciu tego samego PID) i sprawdzanie, czy skrypt faktycznie odpowiedział (zakończył) na pierwsze żądanie „zabicia”. Wykonanie wszystkich tych kontroli może się skomplikować i nie chciałem, aby przykład był zbyt długi i skomplikowany. Możesz zmodyfikować przykład, aby ćwiczyć skrypty powłoki.

Zapisz następujący kod w pliku o nazwie „programctl”, aby był wykonywalny za pomocą polecenia:

chmod 755 programctl

Następnie edytuj plik i dodaj swój kod / skrypt w sekcji przypadku zaczynającej się od „myscript”.

Gdy wszystko będzie już na swoim miejscu, zakładając, że „programctl” znajduje się w bieżącym katalogu, możesz uruchomić swój program za pomocą:

./programctl start

I przestań:

./programctl stop

Twoje zdrowie.

#!/bin/bash
# Description:  A wrapper script used to stop/start another script.

#--------------------------------------
# Define Global Environment Settings:
#--------------------------------------

# Name and location of a persistent PID file

PIDFILE="/tmp/tmpfile-$LOGNAME.txt"

#--------------------------------------
# Check command line option and run...
# Note that "myscript" should not
# provided by the user.
#--------------------------------------

case $1
in
    myscript)
        # This is where your script would go.
        # If this is a routine 'bash' shell script, you can enter
        # the script below as illustrated in the example.  
        # Or you could simply provide the path and parameters
        # to another script such as /dir/name/command -options

        # Example of an embedded script:

        while true
        do
            # do something over and over...
            sleep 1
        done

        # Example of an external script:

        /usr/local/bin/longrun -x
    ;;

    start)
        # Start your script in the background.
        # (Note that this is a recursive call to the wrapper
        #  itself that effectively runs your script located above.)
        $0 myscript &

        # Save the backgound job process number into a file.
        jobs -p > $PIDFILE

        # Disconnect the job from this shell.
        # (Note that 'disown' command is only in the 'bash' shell.)
        disown %1

        # Print a message indicating the script has been started
        echo "Script has been started..."
    ;;

    stop)
        # Read the process number into the variable called PID
        read PID < $PIDFILE

        # Remove the PIDFILE
        rm -f $PIDFILE

        # Send a 'terminate' signal to process
        kill $PID

        # Print a message indicating the script has been stopped
        echo "Script has been stopped..."
    ;;

    *)
        # Print a "usage" message in case no arguments are supplied
        echo "Usage: $0 start | stop"
    ;;
esac

Wypróbowałem zaproponowaną przez ciebie metodę „screen” i działa ona pięknie. Mam jeszcze wypróbować drugą metodę. Dziękuję za Twoją odpowiedź.
light94
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.