Töten eines Shell-Skripts, das im Hintergrund ausgeführt wird

12

Ich habe ein Shell-Skript geschrieben, um ein Verzeichnis mit dem Dienstprogramm inotifywait von inotifyt-tools zu überwachen. Ich möchte, dass dieses Skript kontinuierlich im Hintergrund ausgeführt wird, aber ich möchte es auch stoppen können, wenn dies gewünscht wird.

Um es kontinuierlich laufen zu lassen, habe ich verwendet while true; so was:

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

Ich habe es in einer Datei gespeichert /binund ausführbar gemacht. Um es im Hintergrund laufen zu lassen, habe ich nohup <script-name> &das Terminal benutzt und geschlossen.

Ich weiß nicht, wie ich dieses Skript stoppen soll. Ich habe bei den Antworten sah hier und eine sehr eng verwandte Frage hier .

UPDATE 1: Auf der Grundlage der Antwort von @InfectedRoot unten konnte ich mein Problem mit der folgenden Strategie lösen. Erste Benutzung

ps -aux | grep script_name

und verwenden sudo kill -9 <pid>, um die Prozesse zu beenden. Ich musste dann pgrep inotifywaitund sudo kill -9 <pid>wieder für die zurückgegebene ID verwenden.

Das funktioniert, aber ich denke, das ist ein chaotischer Ansatz. Ich suche nach einer besseren Antwort.

UPDATE 2: Die Antwort besteht darin , 2 Prozesse zu beenden . Dies ist wichtig, da das Ausführen des Skripts in der Befehlszeile zwei Prozesse initiiert, einen das Skript selbst und zwei den inotify-Prozess .

light94
quelle
2
Das Entfernen der -9Option aus killund mit nur killnehmen das Chaos aus ihm heraus.
Sree
1
Bravo. Um wieder etwas '$$' in die Wortspielkasse zu bekommen, möchte ich betonen, dass die -9Option hier total wäre kill. : P
Syntax
Der Vollständigkeit halber gibt es einen sauberen Ansatz, um beide Prozesse gleichzeitig zu beenden: Anstatt die Prozesse separat zu beenden, können Sie die Prozessgruppe des Skripts beenden, um sie sofort zu beenden. Die pgid ist die gleiche wie die pid des Shell - Scripts Hauptprozess und killes gibt Ihnen die pgid mit einem Minus Präfix: kill -- -<pgid>, kill -9 -<pgid>usw.
cg909

Antworten:

5

So verbessern, verwenden killallund kombinieren Sie die Befehle:

ps -aux | grep script_name
killall script_name inotifywait

Oder machen Sie alles in einer Zeile:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait
Creme fraiche
quelle
Ihre obige Lösung funktioniert, aber nicht der einzeilige Befehl. Könnten Sie es bitte überprüfen. Ich erhalte eine Fehlermeldung: kein Prozess
light94
@ light94 Error: no processsollte nur bedeuten, dass du es bereits getötet hast. Sie können es testen, indem Sie zwei andere Programme wie VLC & Geany öffnen und es stattdessen mit ihnen versuchen.
Cremefraiche
Hey @cremefraiche, wie Earler sagte, funktioniert Ihr Code. Aber ich habe nach alternativen Lösungen gesucht und die häufigste Lösung, die ich sehe, ist die Verwendung von kill <pid>. Da mein Skript nicht auf diese Weise endet, bin ich etwas besorgt, wenn mein Code nicht korrekt ist? Kannst du mich bitte führen?
Licht94
Sie können sich auf die sparen , grep -v grepindem grep script_namein grep -e "[s]cript_name".
Nmichaels
3

Listen Sie die Hintergrundjobs mit auf

# jobs

Wählen Sie dann die vorherige Auftragsnummer und führen Sie sie aus

Beispiel

# fg 1 

Geben Sie hier die Bildbeschreibung ein

wird in den Vordergrund bringen.

Beenden Sie es dann mit STRG + C oder auf einfachere Weise, um die PID des Skripts mithilfe von zu ermitteln

ps -aux | grep script_name

Geben Sie hier die Bildbeschreibung ein

Dann töte mit pid

sudo kill -9 pid_number_here
Babin Lonston
quelle
2
Die erste Option ist für meinen Fall nicht gültig, da ich die Shell nach Angabe des Skripts schließe und denke, dass der Befehl jobs nur für dieselbe Shell funktioniert. Außerdem habe ich die zweite Option ausprobiert und sie beendet den Prozess, aber wenn ich pgrep inotifywait mache, kann ich sie dort immer noch als Prozess sehen.
light94
Jobs geben die Liste der Jobs an, auch wenn Sie die Shell schließen. Noch wird der Prozess beendet, es wird da sein.
Babin Lonston
2
Ich habe es versucht, aber es war nicht so.
Light94
@ light94 Du hast recht. Es kam hier häufig vor, dass der jobsBefehl die laufenden Jobs nur in der Shell anzeigte. Sobald ich ein bestimmtes Terminalfenster geschlossen hatte, war der einzige Weg, auf sie zuzugreifen, über ps(siehe oben). Es ist also sicher, dass Sie sich das nicht ausdenken.
Syntaxfehler
2

Sie können mit ps+ grepoder pgreperhalten der Prozess Name / pid ; später benutze killall/ pkillum den Prozessnamen zu töten oder benutze killum pid zu töten. Alle folgenden Schritte sollten funktionieren.

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

Das Wichtigste ist, dass Sie sicherstellen, dass Sie nur die genauen Prozesse beenden , die Sie töten möchten.

pkill/ pgrepentspricht eher einem Muster als dem genauen Namen , ist also gefährlicher; Hier -xwird hinzugefügt, um dem genauen Namen zu entsprechen.

Auch bei der Verwendung von pgrep/ pkillbenötigen Sie möglicherweise

  • -fum der vollständigen Befehlszeile zu entsprechen (wie ps auxauch)
  • -a um auch den Prozessnamen zu drucken.
Hongxu Chen
quelle
Nachdem ich das oben genannte ausgeführt habe, erhalte ich jeweils einen Verwendungsfehler. Ich kopiere das Einfügen und Ersetzen des Skriptnamens. Ich bekomme Verwendung für Kill als Ausgabe
light94
Läuft script_name? Es funktioniert für mich (Debian Jessie)
Hongxu Chen
ja es läuft und Ihre Lösung ist fast die gleiche wie @cremefraiche Lösungen oben, also hatte ich erwartet, dass es genauso funktioniert: /
light94
Ja, ich habe die möglichen Möglichkeiten etwas zusammengefasst, insbesondere das Hinzufügen von pgrep/ pkill. Wenn das immer noch schief geht, können Sie es pgrepohne -x versuchen . Grundsätzlich halte ich es nicht für gut, den Prozess innerhalb eines Zeilenbefehls abzubrechen, ohne zu überprüfen, ob andere Prozesse übereinstimmen.
Hongxu Chen
1

Wie Sie wahrscheinlich sehen können, gibt es viele Möglichkeiten, dies zu tun.

In Bezug auf Ihr "UPDATE # 2" - im Allgemeinen werden durch das Beenden eines Prozesses in einer Eltern-Kind-Hierarchie im Allgemeinen alle zugehörigen Prozesse beendet. Es gibt jedoch viele Ausnahmen. Idealerweise möchten Sie das letzte "Kind" in einem Prozessbaum beenden. Die Eltern dieses Kindes sollten dann beendet werden, wenn keine anderen Aufgaben ausgeführt werden müssen. Wenn Sie jedoch einen Elternteil töten, sollte das Signal an Kinder weitergeleitet werden, wenn der Elternteil stirbt, und die Kinder sollten ebenfalls beendet werden. Es gibt jedoch Fälle, in denen die untergeordneten Prozesse das Signal möglicherweise ignorieren (über Fallen oder ähnliche Mechanismen) und fortgesetzt werden Ein Wille auszuführen wird vom 'init'-Prozess (oder ähnlichem) geerbt. Aber dieses Thema des Prozessverhaltens kann komplex werden und ich lasse es einfach dort ...

Eine Methode, die mir gefällt, wenn ich kein Steuerungsskript verwenden möchte (siehe Beschreibung), ist die Verwendung des Dienstprogramms 'screen', um den Prozess zu starten und zu verwalten. Der Befehl 'screen' ist voll von Funktionen und kann einige Zeit in Anspruch nehmen, um ihn zu beherrschen. Ich möchte Sie dazu ermutigen, die Manpage "Bildschirm" zu lesen, um eine vollständige Erklärung zu erhalten. Ein schnelles Beispiel, um einen Prozess im Hintergrund zu starten, wäre der Befehl:

Bildschirm -d -m / Pfad / zu / Programm

Dies startet "/ path / to / program" innerhalb einer "Bildschirm" -Sitzung.

Sie können Ihre laufende Sitzung mit dem folgenden Befehl anzeigen:

Bildschirm -ls

Und Sie können sich jederzeit mit dem folgenden Befehl wieder mit Ihrem laufenden Programm verbinden:

Bildschirm -r

Und dann beenden Sie es einfach mit einem ^ C oder was auch immer.

Neben der Schönheit, nach Belieben wieder eine Verbindung zu Ihrem Prozess herstellen zu können, besteht darin, dass der Bildschirm alle stdout () erfasst, die Ihr Programm möglicherweise erzeugt.


Meine persönliche Präferenz in diesen Angelegenheiten ist jedoch ein Kontrollprogramm, das das Starten und Stoppen eines Prozesses verwaltet. Dies kann etwas kompliziert werden und erfordert einige wohl komplizierte Skripte. Und wie bei jedem Skript gibt es Dutzende guter Möglichkeiten, dies zu tun. Ich habe ein Bash-Beispiel für eine Methode beigefügt, mit der ich Anwendungen routinemäßig starte und stoppe. Wenn Ihre Aufgabe einfach ist, können Sie sie direkt in das Steuerungsskript einfügen - oder Sie können dieses Steuerungsskript ein anderes externes Programm aufrufen lassen. Beachten Sie, dass dieses Beispiel in Bezug auf die Verwaltung des Prozesses keineswegs umfassend ist. Ich habe die Möglichkeit von Szenarien wie den folgenden ausgelassen: Stellen Sie sicher, dass das Skript nicht bereits ausgeführt wird, wenn Sie die Option "Start" verwenden, und überprüfen Sie, ob die ausgeführte PID tatsächlich der von Ihnen gestartete Prozess ist (z. B. Ihr Skript wurde nicht ausgeführt). Es ist gestorben und ein anderer Prozess wurde mit derselben PID gestartet. Dabei wurde überprüft, ob das Skript bei der ersten Anforderung zum Beenden tatsächlich geantwortet (beendet) hat. Das Durchführen all dieser Überprüfungen kann kompliziert werden, und ich wollte das Beispiel nicht zu lang und komplex machen. Möglicherweise möchten Sie das Beispiel ändern, um Ihr Shell-Scripting zu üben.

Speichern Sie den folgenden Code in einer Datei namens "programctl" und machen Sie ihn mit dem Befehl ausführbar:

chmod 755 programctl

Bearbeiten Sie dann die Datei und fügen Sie Ihren Code / Ihr Skript in den Fallabschnitt ein, der mit "myscript" beginnt.

Sobald alles vorhanden ist und das "programctl" sich im aktuellen Verzeichnis befindet, können Sie Ihr Programm starten mit:

./programctl start

Und hör auf mit:

./programctl stop

Prost.

#!/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
Christopher Morris
quelle
Ich habe die von Ihnen vorgeschlagene "Bildschirm" -Methode ausprobiert und sie funktioniert wunderbar. Ich muss noch die zweite Methode ausprobieren. Danke für die Antwort.
light94