Befehl nach Ablauf einer bestimmten Zeit ausführen?

23

Gibt es eine Möglichkeit, zeitbasierte Befehle auszuführen, wenn ich einen langen Prozess ausführe?

Zum Beispiel führe ich einen sehr langen Prozess aus, der ungefähr 10 Minuten dauert.

Nach 5 Minuten möchte ich einen separaten Befehl ausführen. Zur Veranschaulichung könnte der separate Befehl sein:echo 5 minutes complete

(Hinweis: Ich möchte keine Fortschritte bei der Ausführung des Befehls machen, sondern nur Befehle, die nach festgelegten Intervallen ausgeführt werden.)

Ist es möglich?

Aniket Bhattacharyea
quelle
1
In den meisten Fällen wissen Sie nicht im Voraus, wie lange ein Vorgang dauern wird.
Choroba
Was meinst du mit "zeitbasierten Befehlen"? Möchten Sie eine Fortschrittsanzeige erstellen? Basierend auf der aktuellen Formulierung Ihrer Frage können Sie einfach den Langzeitbefehl in den Hintergrund stellen und dann die Systemuhr anzeigen. Bitte klären Sie.
Wildcard
@Wildcard habe ich in der Frage geklärt. Keine Fortschrittsanzeige. Nach einigen Intervallen werden Befehle ausgeführt
Aniket Bhattacharyea
1
"5 minutes complete" ist ein merkwürdiges Echo. Nicht "5 Minuten sind vergangen"? Nicht "Die Zeit ist _____"? Möchten Sie nur sicherstellen, dass ein bestimmtes Zeitintervall seit dem Start Ihres lang laufenden Befehls verstrichen ist, bevor ein anderer willkürlicher Befehl ausgeführt wird? Wenn ja, warum nicht einfach laufen longcommand & sleep 300 && command-to-do-after-five-minutes? (Eigentlich ist es wahrscheinlich das, wonach Sie suchen.) Beachten Sie, dass Ihre Klärung nicht ausreichte, da Sie gleich zwei Clock-Progress-Bar-Implementierungen erhalten haben.
Wildcard

Antworten:

29

Renn einfach:

long-command & sleep 300; do-this-after-five-minutes

Der do-this-after-five-minuteswird nach fünf Minuten ausgeführt. Die long-commandläuft im Hintergrund.

Platzhalter
quelle
10
Ich benutze normalerweise sleep 5m && foobar, wenn ich meine Meinung ändere und ^ C das sleep, wird der nächste Befehl nicht ausgeführt.
Peter Cordes
@PeterCordes, als ich es mit zB sleep 3; lsund getestet habe ^C, läuft der zweite Befehl sowieso nicht. Nicht wirklich sicher, warum und vielleicht funktioniert es in einer nicht interaktiven Shell anders?
Wildcard
1
@PeterCordes Nicht ganz - es sei denn, es unterscheidet sich je nach Shell. Wenn sleep 5m && echoSie in den Ruhemodus wechseln, wird nur der sleepBefehl angehalten. Der Rest der Befehlszeile wird ausgeführt, aber da sie sleepangehalten wurde, wurde sie nicht erfolgreich beendet und der Teil danach &&wird übersprungen. Versuchen Sie es mit Suspendieren sleep 5 || echo hellound es hellowird direkt nach dem Drücken von angezeigt ^Z.
HDV
1
@hvd: Yup, du hast recht und ich irre mich wieder>. <mit bash4. Anscheinend ist es zu lange her, dass ich mich für && als Ghetto entschieden habe at(1), für Dinge wie die sleep 30m && mplayer something.mp3Erinnerung sleep 90m && fgan einen Alarm oder um später einen plattenintensiven Befehl wieder aufzunehmen. Also eigentlich sleep 5m && echo helloist das schön, weil ohne Ausführen des folgenden Befehls überhaupt ^Zpausiert sleep. Wenn Sie in der Lage sein möchten, den gesamten zusammengesetzten Befehl vor dem Beenden des Ruhezustands anzuhalten / fortzusetzen, verwenden Sie ( sleep 2 && echo hello ).
Peter Cordes
@hvd: Das &&Idiom können Sie absolut sicher sein , Sie töten den Prozess nicht direkt nach dem Schlaf läuft (da kann man ^Znicht den Befehl auszuführen, statt eine zu riskieren ^C). Dies ist nützlich im sleep && fgoder sleep && bgAnwendungsfall, wenn der Befehl, der fortgesetzt wird, auf halbem Weg durch etwas Langsames ist.
Peter Cordes
5

Sie könnten dieses Skript verwenden:

#!/bin/bash

TEMPFILE="$(mktemp)"
STARTTIME="$(date +%s)"
(./longprocess; rm -f "$TEMPFILE") &
while [ -f "$TEMPFILE" ]; do
    sleep 1s
    NOW="$(date +%s)"
    if (( (NOW-STARTTIME) % 300 == 0 )); then
        echo "$(( (NOW-STARTTIME)/60 )) minute(s) elapsed"
    fi
done
echo "Done!!!"

Es führt Ihre longprocessin einer sub-shellund überwacht dann zuvor erstellte "Lock" -Datei auf Existenz.

Serge
quelle
2
Überprüfen Sie man bashauf SECONDS. Sie können SECONDS=0beim Start des Skripts SECONDS=$((date +%s))date
@yeti, danke für den Hinweis, das wusste ich nicht.
Serge
@yeti, wenn Sie sich darauf verlassen wollen, dass die Zeit sleep 1simmer genau eine Sekunde später ist als vorher sleep 1s... dann sind Sie Opfer von Lügen geworden, die Programmierer über die Zeit glauben . (In diesem Fall können Sie jedoch auch einfach einen Fortschrittsbalken für die Hashmarkierung oder etwas
Wildcard
@Wildcard ... habe ich gar nicht erwähnt sleep!
@yeti, richtig du bist. Danke für den Tipp zu SECONDS! :)
Wildcard
4

Es gibt einen Einzeiler dafür:

( ( sleep $TIMEOUT ; echo "5 minutes complete") & $COMMAND )

In Ihrem Fall ist TIMEOUT = 5m und COMMAND ist der lange Befehl.

Siehe auch meine Antwort auf diesen Beitrag Timeout mit "Service-Netzwerk-Neustart"

Kaffeebecher
quelle
Das Starten einer Subshell im Vordergrund und das anschließende Ausführen des Befehls erscheint unnötig kompliziert. Ich würde die äußeren Klammern und die exec entfernen.
Glenn Jackman
1
@glennjackman Auf diese Weise kann die gesamte Prozedur abgebrochen werden, indem STRG + C (zum Beispiel) an die Haupt-Sub-Shell (die äußerste) gesendet wird.
Kaffeebecher
1
Aber mit exec haben Sie keine Subshell mehr, sondern nur noch den Befehl. Das übergeordnete Element des Befehls ist die Shell, die das Skript so ausführt, als hätten Sie überhaupt keine Subshell verwendet.
Glenn Jackman
3
#! /bin/bash

( sleep 4 ) & # <-- The long running process.

seconds=1
while jobs %1 &>/dev/null ; do
    echo $((seconds++)) seconds complete
    sleep 1    
done
echo Done.

jobs %1 schlägt fehl, sobald der Job% 1 gestoppt wurde.

Beachten Sie, dass $ seconds für längere Zeit möglicherweise nicht mit der Echtzeit synchronisiert ist. Es ist besser, die Startzeit zu speichern und die Differenz zur tatsächlichen Zeit zu berechnen.

Choroba
quelle