Befehle wie in einer Warteschlange ausführen

42

Ich muss eine Menge von verschiedenen Dateien in verschiedene Ordner kopieren. Ich kann alle meine Kopierbefehle zu einem Bash-Skript hinzufügen und das dann ausführen, aber dann muss ich warten, bis es beendet ist, wenn ich dieser "Kopierwarteschlange" weitere Befehle hinzufügen möchte.

Gibt es eine Möglichkeit, Befehle als Warteschlange auszuführen und dieser Warteschlange weitere Befehle hinzuzufügen, während die Dinge laufen?

Anders erklärt, möchte ich eine lange laufende Aufgabe starten. Während das läuft, möchte ich ein anderes starten, das nicht wirklich startet, bis das erste fertig ist. Und dann fügen Sie eine weitere nach der letzten hinzu und so weiter. Ist das irgendwie möglich

Svish
quelle
3
Darf ich vorschlagen, dass Sie eine Antwort akzeptieren, die Ihr Problem löst. Es scheint, dass es ein paar gibt, die für Sie arbeiten würden.
Holmb
2
Ja du darfst, und ich wollte. Das Problem ist, dass ich dies gefragt habe, als ich in einer Firma arbeitete, die Macs verwendete. Ich arbeite dort nicht mehr und habe auch keinen Mac mehr, so dass ich keinen davon testen kann. Ich fühle mich nicht wohl, wenn ich eine als Antwort markieren kann, wenn ich nicht testen kann, ob sie tatsächlich funktioniert. Deshalb habe ich gehofft, dass die Leute die Antworten abstimmen, die sie für gut finden, damit die "Antwort" auf diese Weise entstehen kann stattdessen.
Svish

Antworten:

25

Das atDienstprogramm, das am besten dafür bekannt ist, Befehle zu einem bestimmten Zeitpunkt auszuführen, verfügt auch über eine Warteschlangenfunktion und kann aufgefordert werden, Befehle jetzt auszuführen. Es liest den Befehl, der von der Standardeingabe ausgeführt werden soll.

echo 'command1 --option arg1 arg2' | at -q myqueue now
echo 'command2 ...' | at -q myqueue now

Der batchBefehl ist äquivalent zu at -q b -m now( -mwas bedeutet, dass die Befehlsausgabe, falls vorhanden, an Sie gesendet wird, wie dies bei cron der Fall ist). Nicht alle Unix-Varianten unterstützen Warteschlangennamen ( -q myqueue). Sie können auf eine einzelne Warteschlange mit dem Namen beschränkt sein b. Linux atist auf Warteschlangennamen mit einem Buchstaben beschränkt.

Gilles 'SO - hör auf böse zu sein'
quelle
2
Zumindest unter Linux scheint dies nicht auf den Abschluss des vorherigen Befehls zu warten.
WDS
@wds Funktioniert für mich mit Debian Wheezy. Wo und wie ist es für dich kaputt gegangen? Beachten Sie, dass at nur darauf wartet, dass der Befehl beendet wird. Wenn der Befehl Unterprozesse startet, die ihren übergeordneten Prozess überleben, wartet at nicht auf die Unterprozesse.
Gilles 'SO- hör auf böse zu sein'
Ich habe CentOS 6 ausprobiert. Ich habe es nur mit einem einfachen "sleep x" getestet. Ich bin mir nicht sicher, wie das ausgeführt werden soll, aber ich gehe davon aus, dass eine Subshell gestartet wird und diese Subshell auf das Ergebnis warten sollte (ein Sleep-Aufruf wird nicht sofort zurückgegeben).
WDS
Neben den Warteschlangennamen erscheint auch 'now' nicht als Standard: bash on ubuntu beschwert sich darüber ...
winwaed
@winwaed Oh, es ist nownicht -t now, sorry. Ich hatte nicht bemerkt, dass ich einen Fehler im Beispielcode hatte.
Gilles 'SO- hör auf böse zu sein'
23

Fügen Sie &an das Ende Ihres Befehls einen Text an, um ihn in den Hintergrund und anschließend in den Hintergrund zu verschieben, waitbevor Sie den nächsten Befehl ausführen . Zum Beispiel:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...
Vortico
quelle
2
Yay! Diese Syntax ist fantastisch, wenn Sie etwas angepfiffen haben und dann Stackoverflow nach einer Möglichkeit durchsuchen, etwas danach in die Warteschlange zu stellen
Erik Aronesty,
2
Wenn Sie in der Zwischenzeit Ihre Meinung zu einem dieser Befehle ändern, was kann getan werden, um den waitVorgang abzubrechen ? Es scheint für alle meine Killshots undurchlässig zu sein.
Ken Williams
1
Sie töten nur den Befehl. Der waitstoppt einfach den Job, bis die vorherigen beendet sind.
Gert Sønderby
Funktioniert das mit nohup?
Michael
11

Sowohl Brad als auch Mankoffs Lösungen sind gute Vorschläge. Eine andere, die einer Kombination von beiden ähnelt, wäre, GNU Screen zu verwenden, um Ihre Warteschlange zu implementieren. Dies hat den Vorteil, dass es im Hintergrund ausgeführt werden kann, dass Sie es jederzeit überprüfen können und dass beim Einreihen neuer Befehle diese einfach in den Puffer eingefügt werden, damit sie ausgeführt werden, nachdem die vorherigen Befehle beendet wurden.

Erster Lauf:

$ screen -d -m -S queue

( Übrigens , jetzt ist ein guter Zeitpunkt, um mit einigen großartigen .screenrc-Dateien zu spielen. )

Dadurch wird eine Hintergrundbildschirmsitzung für die von Ihnen angegebene Warteschlange erstellt.

Stellen Sie nun so viele Befehle in die Warteschlange, wie Sie möchten:

screen -S queue -X stuff "echo first; sleep 4; echo second^M"

Ich führe oben mehrere Befehle nur zum Testen aus. Ihr Anwendungsfall würde wahrscheinlich eher so aussehen:

screen -S queue -X stuff "echo first^M"
screen -S queue -X stuff "echo second^M"

Beachten Sie, dass das "^ M" in meiner Zeile oben eine Möglichkeit ist, eine eingebettete neue Zeile zu erhalten, die später interpretiert wird, nachdem der Bildschirm sie in Ihre vorhandene Bash-Shell eingefügt hat. Verwenden Sie "CTL-V", um diese Sequenz abzurufen.

Es wäre ziemlich einfach, einige einfache Shell-Skripte zu erstellen, um dies zu automatisieren und Befehle in die Warteschlange zu stellen. Wann immer Sie den Status Ihrer Hintergrundwarteschlange überprüfen möchten, können Sie eine erneute Verbindung herstellen über:

screen -S queue -r

Technisch gesehen müssen Sie Ihre Bildschirmsitzung nicht einmal benennen, und es funktioniert einwandfrei, aber sobald Sie sich darauf einlassen, möchten Sie sowieso immer eine laufen lassen. ;-)

Wenn Sie dies tun, ist es natürlich auch sinnvoll, eine der aktuellen Windows-Warteschlangen zu benennen und Folgendes zu verwenden:

screen -S queue -p queue -X stuff "command"
Jordan
quelle
Es sieht so aus, als würde der Befehl in der laufenden Bildschirmsitzung nur "getippt", sodass dieser Befehl gestartet wird, wenn die Shell nach Beendigung des ersten Befehls wieder die Kontrolle erlangt. Mit anderen Worten, es entspricht der @ mankoff-Lösung, verwendet jedoch ein ausgefalleneres Tool für die Eingabe.
Ken Williams
Ziemlich viel - der Hauptunterschied ist, dass die Bildschirmlösung eine bessere Automatisierung unterstützt. Sie können einige Dinge von Hand erledigen, andere Dinge mit Skripten, alle mit einer einfachen Oberfläche.
Jordanien
@Jordan Sehr gut, es funktioniert bei mir. Aber was ist "Zeug" nach -X?
Konstantin
@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - Zeug, was auch immer im Terminal folgt, als ob Sie es eingegeben hätten)
Jordan
@Jordan Oh, ich verstehe jetzt. Es ist ein Befehl. Ich dachte, es ist eine beliebige Zeichenfolge.
Konstantin
8

Es gibt ein Dienstprogramm, das ich mit großem Erfolg für genau den Anwendungsfall verwendet habe, den Sie beschreiben. Vor kurzem habe ich meinen primären Laptop auf neue Hardware umgestellt, wobei einige Dateien auf einen NAS und der Rest auf den neuen Computer verschoben wurden.

So habe ich es gemacht.

  1. Richten Sie alle an einer Netzwerkverbindung beteiligten Computer so ein, dass sie sich gegenseitig erreichen können.

  2. Installieren Sie auf dem Computer, von dem Sie Dateien verschieben (im Folgenden als Quellcomputer bezeichnet), rsync mit apt-get install rsyncund den Task-Spooler mit geheimer Sauce (Website http://vicerveza.homeunix.net/~viric/soft/ts/ ). Wenn Sie unter Debian arbeiten, tslautet der Paketname für task-spoolerund die ausführbare Datei wird umbenannt tsp, um Namenskonflikte mit der tsausführbaren Datei aus dem moreutilsPaket zu vermeiden . Das von der Website verlinkte Debian-Paket ist perfekt auf Ubuntu mit dpkg -i task-spooler_0.7.3-1_amd64.deboder ähnlichem installierbar .

  3. Stellen Sie außerdem sicher, dass auf allen Computern SSH mit installiert ist apt-get install openssh-server. Auf dem Quellcomputer müssen Sie SSH einrichten, um eine kennwortlose Anmeldung auf den Zielcomputern zu ermöglichen. Die am häufigsten verwendete Methode ist die Authentifizierung mit öffentlichen Schlüsseln ssh-agent(siehe https://www.google.se/search?q=ssh+public+key+authentication für Beispiele hierfür), aber ich verwende manchmal eine einfachere Methode, die einfach funktioniert auch mit Passwortauthentifizierung. Fügen Sie Ihrer SSH-Client-Konfiguration Folgendes hinzu (entweder ~/.ssh/configoder /etc/ssh/ssh_config):

    Host *
         ControlMaster auto
         ControlPath ~/.ssh/master-%r@%h:%p
    

    Öffnen Sie dann ein Terminal auf dem Quellcomputer, melden Sie sich mit an ssh target1, authentifizieren Sie sich wie gewohnt und lassen Sie dieses Terminal geöffnet. Beachten Sie, dass es eine Socket-Datei mit dem Namen ~/.ssh/master-user@target1:22gibt. Dies ist die Datei, die eine authentifizierte Mastersitzung geöffnet hält und nachfolgende kennwortlose Verbindungen zulässt user(solange die Verbindung denselben Ziel-Hostnamen und -Port verwendet).

    Zu diesem Zeitpunkt müssen Sie sicherstellen, dass Sie sich anmelden können, ohne zur Authentifizierung bei den Zielcomputern aufgefordert zu werden.

  4. Führen Sie nun ts rsync -ave ssh bigfile user@target1:eine einzelne Datei oder ts rsync -ave ssh bigdir user@target1:ein Verzeichnis aus. Bei rsync ist es wichtig, keinen abschließenden Schrägstrich in das Verzeichnis (im bigdirVergleich zu bigdir/) einzufügen. Andernfalls geht rsync davon aus, dass Sie das Äquivalent zu den bigdir/*meisten anderen Tools gemeint haben.

    Der Task-Spooler gibt die Eingabeaufforderung zurück und lässt Sie viele dieser Befehle nacheinander in die Warteschlange stellen. Überprüfen Sie die Ausführungswarteschlange mit tsohne Argumente.

Der Task-Spooler verfügt über zahlreiche Funktionen, z. B. das Neuanordnen der Ausführungswarteschlange, das Ausführen eines bestimmten Jobs nur, wenn ein anderer Job erfolgreich ausgeführt wurde usw. Lesen Sie die Hilfe zu ts -h. Manchmal überprüfe ich die Befehlsausgabe, während sie ausgeführt wird ts -c.

Es gibt andere Methoden, um dies zu tun, aber für Ihren Anwendungsfall enthalten sie alle Task-Spooler. Ich entscheide mich für die Verwendung von rsync over SSH, um Datei-Zeitstempel beizubehalten, die beim Kopieren über SMB nicht möglich wären.

holmb
quelle
vielen Dank für Ihre Antwort habe ich kenne nicht ts, scheinen sehr leistungsfähig und einfach zu bedienen, einfach gegabelt auf Github autotoolized und debianisierten.
Alex
@Alex Ich habe mir Ihre Gabel angesehen und war besonders daran interessiert, was in Bezug auf das Autotooling und die Debianisierung der Originalquelle getan wurde. Wenn ich mir Ihre Commits ansehe, war es nicht ohne Weiteres als Unterschied zur Originalquelle verfügbar. Würde es Ihnen etwas ausmachen, neue Commits zu erzwingen, die in Schritt 1. die ursprüngliche Quelle importieren und 2. mit Ihren Ergänzungen festschreiben? Es hilft jemandem, der in Ihrer Quelle browst, Ihren Ergänzungen zur Originalquelle (leichter) zu vertrauen. Dies ist eines der wenigen Pakete, die ich nicht von einem PPA oder ähnlichem installiere. Es wäre also schön, wenn Sie es selbst mit Ihrer Gabel bauen würden.
Holmb
du weißt, ich muss meine Brille wechseln, ich habe den Teil deiner Antwort über das Vorhandene verloren task-spooler:-) ... jedenfalls ist das in deinem Kommentar ein sehr guter Vorschlag, den ich sehr bald machen werde.
Alex
getan, gelöscht und neu erstellt das Repository mit progressiven Commits, nur ein paar Fehler (sollte mehr lokale Commits vor Push verstoffwechseln)
Alex
4

Ich brauche sowas auch ziemlich oft. Ich habe ein kleines Hilfsprogramm geschrieben after, das einen Befehl ausführt, wenn ein anderer Prozess beendet ist. Es sieht aus wie das:

#!/usr/bin/perl

my $pid = shift;
die "Usage: $0 <pid> <command...>" unless $pid =~ /^\d+$/ && @ARGV;

print STDERR "Queueing process $$ after process $pid\n";
sleep 1 while -e "/proc/$pid";
exec @ARGV;

Sie führen es dann so aus:

% command1 arg1 arg2 ...  # creates pid=2853
% after 2853 command2 arg1 arg2 ... # creates pid=9564
% after 9564 command3 arg1 arg2 ...

Dies hat gegenüber einigen anderen Ansätzen den großen Vorteil, dass der erste Job nicht auf besondere Weise ausgeführt werden muss. Sie können jeden Prozess mit Ihrem neuen Job verfolgen.

Ken Williams
quelle
Nettes Skript @ken. Ich würde allerdings empfehlen, Task Spooler auszuprobieren, da dies von Zeit zu Zeit erforderlich zu sein scheint. Siehe meine Antwort.
Holmb
Sieht gut aus, danke. Das einzige, was TS vermisst, scheint die Fähigkeit zu sein, Jobs zu verfolgen, die nicht unter TS gestartet wurden. Obwohl ich vermute, dass Sie einen neuen TS-Job starten können, dessen Zweck es ist, nur auf den Abschluss eines vorhandenen Prozesses zu warten.
Ken Williams
Tatsächlich. Das ist ein nützlicher Aspekt Ihrer Lösung.
Holmb
2

Befehl1 && Befehl2

  • Das "&&" bedeutet, dass Befehl2 erst ausgeführt wird, nachdem Befehl1 mit dem Rückkehrcode 0 beendet wurde
  • Hinweis: a || bedeutet, dass Befehl2 nur ausgeführt wird, nachdem Befehl1 mit einem anderen Rückkehrcode als 0 beendet wurde
Naftuli
quelle
1

Sie können einfach Befehle zur Befehlszeile der Shell hinzufügen, die bereits einen Befehl ausführt.

Entweder sorgfältig tippen oder anderswo tippen, überprüfen und ausschneiden und einfügen. Selbst wenn der aktuelle Befehl Ausgabezeilen spuckt, können Sie immer noch etwas Neues eingeben, die Eingabetaste drücken und es wird ausgeführt, wenn alle Befehle ausgeführt oder eingegeben wurden, bevor es abgeschlossen ist.

Beispiel folgt. Sie können das Ganze ausschneiden und einfügen oder die nachfolgenden Befehle eingeben, während der erste ausgeführt wird (wenn Sie wirklich sehr schnell tippen), oder wahrscheinlicher, während der zweite ausgeführt wird:

echo "foo"
sleep 10; echo "bar"
sleep 3
echo "baz"

quelle
0

Der hackigste Weg, den ich mir vorstellen kann, ist es, den Warteschlangenstatus mit einfachen Sperrdateien in / tmp beizubehalten. Wenn file1.sh seine cp-Befehle ausführt, behält es eine Sperrdatei (oder einen Hardlink) in / tmp bei. Wenn es fertig ist, löschen Sie die Datei. Jedes andere Skript muss in / tmp nach Sperrdateien mit dem von Ihnen gewählten Benennungsschema suchen. Natürlich ist dies mit allen Arten von Fehlern behaftet, aber es ist trivial einzurichten und kann vollständig innerhalb von Bash durchgeführt werden.

Brad Clawsie
quelle
In der Praxis schwierig zu verwenden, da jeder Job, den Sie ausführen möchten, geändert werden muss, um Sperrdateien zu speichern. Außerdem muss bekannt sein (oder als Parameter akzeptieren), welche Sperrdateien verwendet werden sollen. Wenn immer möglich, ist es am besten, die Warteschlange außerhalb des Jobs zu haben.
Ken Williams