Wie führe ich einen Befehl aus, nachdem ein bereits ausgeführtes, vorhandenes beendet wurde?

32

Wenn ich ein Skript starte, das lange dauern wird, wird es mir unweigerlich klar, nachdem ich das Skript gestartet habe, und ich wünschte, ich hätte eine Möglichkeit, eine Art Warnung zu erstellen, sobald es abgeschlossen ist.

Also zum Beispiel, wenn ich laufe:

really_long_script.sh

und drücken Sie die Eingabetaste ... Wie kann ich einen weiteren Befehl ausführen, wenn dieser beendet ist?

mlissner
quelle

Antworten:

45

Sie können mehrere Befehle durch trennen ;, damit sie nacheinander ausgeführt werden. Beispiel:

really_long_script.sh ; echo Finished

Wenn Sie das nächste Programm nur ausführen möchten, wenn das Skript mit dem Rückkehrcode 0 beendet wurde (was normalerweise bedeutet, dass es korrekt ausgeführt wurde), dann:

really_long_script.sh && echo OK

Wenn Sie das Gegenteil wünschen (dh nur fortfahren, wenn der aktuelle Befehl fehlgeschlagen ist), dann:

really_long_script.sh || echo FAILED

Sie könnten Ihr Skript in einem Hintergrund laufen (aber Vorsicht, Skripte Ausgang ( stdoutund stderr) würde weiterhin das Terminal gehen , wenn Sie es irgendwo Redirect), und dann waitfür sie:

really_long_script.sh &
dosomethingelse
wait; echo Finished

Wenn Sie das Skript bereits ausgeführt haben, können Sie es mit anhalten Ctrl-Zund dann Folgendes ausführen:

fg ; echo Finished

Wobei fgder angehaltene Prozess in den Vordergrund gerückt wird ( bgwürde ihn im Hintergrund laufen lassen, ähnlich wie bei "Begonnen mit" &)

ein Land
quelle
@mlissner meine Antwort Expanded diesen Fall abzudecken
Aland
8
Da fgder Wert des Jobs zurückgegeben wird, der fortgesetzt wird, können Sie ihn verwenden fg && echo Finished, um sicherzustellen, dass der Befehl erfolgreich ausgeführt wird, bevor Sie den anderen Befehl ausführen.
Palswim
13

Sie können auch die Jobsteuerung von bash verwenden. Wenn du angefangen hast

$ really_long_script.sh

Drücken Sie dann Strg + Z, um es anzuhalten:

^Z
[1]+  Stopped                 really_long_script.sh
$ bg

um den Job im Hintergrund neu zu starten (so als ob er mit gestartet würde really_long_script.sh &). Dann können Sie mit auf diesen Hintergrundjob warten

$ wait N && echo "Successfully completed"

Dabei ist N die Job-ID (wahrscheinlich 1, wenn Sie keine anderen Hintergrundjobs ausgeführt haben), die ebenfalls wie [1]oben angezeigt wird .

Jaap Eldering
quelle
11

Wenn der Prozess auf dem aktuellen tty nicht ausgeführt wird, versuchen Sie Folgendes:

watch -g ps -opid -p <pid>; mycommand
Marinos An
quelle
2
Ich liebe den kreativen Einsatz von watch. Super kurze Antwort und man lernt eine neue nützliche Art der Verwendung der Uhr
Piotr Czapla
2

Es stellt sich heraus, dass dies nicht so schwierig ist: Sie können einfach den nächsten Befehl in das Fenster eingeben, während der vorhandene ausgeführt wird, die Eingabetaste drücken und wenn der erste beendet ist, wird der zweite Befehl automatisch ausgeführt.

Ich schätze, es gibt elegantere Möglichkeiten, aber das scheint zu funktionieren.

Bearbeiten, um hinzuzufügen, dass Sie, wenn Sie eine Warnung ausführen möchten, nachdem der Befehl ausgeführt wurde, diese Aliase in .bashrc erstellen und dann ausführen können, alertwährend sie ausgeführt wird:

 alias alert_helper='history|tail -n1|sed -e "s/^\s*[0-9]\+\s*//" -e "s/;\s*alert$//"'
 alias alert='notify-send -i gnome-terminal "Finished Terminal Job" "[$?] $(alert_helper)"'
mlissner
quelle
7
Es sei denn, das ausgeführte Skript erwartet eine Eingabe an einem Punkt.
Daniel Beck
@ Daniel - ja ... das ist ein Problem. Schießen.
mlissner
1

Vor einiger Zeit habe ich ein Skript geschrieben, um auf das Ende eines anderen Prozesses zu warten. NOISE_CMDkönnte sowas sein notify-send ..., vorausgesetzt das DISPLAYist richtig eingestellt.

#!/bin/bash

NOISE_CMD="/usr/bin/mplayer -really-quiet $HOME/sfx/alarm-clock.mp3"

function usage() {
    echo "Usage: $(basename "$0") [ PROCESS_NAME [ PGREP_ARGS... ] ]"
    echo "Helpful arguments to pgrep are: -f -n -o -x"
}

if [ "$#" -gt 0 ] ; then
    PATTERN="$1"
    shift
    PIDS="$(pgrep "$PATTERN" "$@")"

    if [ -z "$PIDS" ] ; then
        echo "No process matching pattern \"$PATTERN\" found."
        exit
    fi

    echo "Waiting for:"
    pgrep -l "$PATTERN" "$@"

    for PID in $PIDS ; do
        while [ -d "/proc/$PID" ] ; do
            sleep 1
        done
    done
fi

exec $NOISE_CMD

Machen Sie einfach sofort ein paar Geräusche. Dieses Verhalten ermöglicht der Einfachheit halber Folgendes (sagen wir, Sie rufen das folgende Skript auf alarm.sh):

apt-get upgrade ; ~/bin/alarm.sh

Natürlich können Sie mit einem solchen Skript viele witzige Dinge tun, zum Beispiel eine Instanz alarm.shauf eine Instanz von warten lassen alarm.sh, die auf einen anderen Befehl wartet. Oder führen Sie einen Befehl aus, nachdem eine Aufgabe eines anderen angemeldeten Benutzers beendet wurde ...>: D

Eine frühere Version des obigen Skripts könnte interessant sein, wenn Sie eine Abhängigkeit von pgrepProzess-IDs vermeiden und akzeptieren möchten, diese selbst zu suchen:

#!/bin/bash

if [ "$#" -lt 2 -o ! -d "/proc/$1" ] ; then
  echo "Usage: $(basename "$0") PID COMMAND [ ARGUMENTS... ]"
  exit
fi

while [ -d "/proc/$1" ] ; do
  sleep 1
done

shift
exec "$@"

Leicht vom Thema abweichend, aber in ähnlichen Situationen nützlich: Möglicherweise reptyrist ein Tool von Interesse , das einen Prozess aus einer (übergeordneten) Shell "stiehlt" und in der aktuellen Shell ausführt. Ich habe ähnliche Implementierungen ausprobiert und bin reptyrdie schönste und zuverlässigste für meine Zwecke.

Piscoris
quelle