Ich habe eine allgemeine Frage, die möglicherweise auf Missverständnisse im Umgang mit Prozessen unter Linux zurückzuführen ist.
Für meine Zwecke definiere ich ein 'Skript' als einen Ausschnitt aus Bash-Code, der in einer Textdatei gespeichert ist und für den aktuellen Benutzer die Ausführungsberechtigung aktiviert hat.
Ich habe eine Reihe von Skripten, die sich nacheinander aufrufen. Der Einfachheit halber werde ich sie Skripte A, B und C nennen. Skript A führt eine Reihe von Anweisungen aus und hält dann an, führt dann Skript B aus, hält dann an und führt dann Skript C aus. Mit anderen Worten, die Reihe of steps ist ungefähr so:
Führen Sie Skript A aus:
- Reihe von Aussagen
- Pause
- Führen Sie Skript B aus
- Pause
- Führen Sie Skript C aus
Ich weiß aus Erfahrung, dass, wenn ich Skript A bis zur ersten Pause ausführe und dann Änderungen in Skript B vornehme, sich diese Änderungen auf die Ausführung des Codes auswirken, wenn ich zulasse, dass er fortgesetzt wird. Wenn ich Skript C bearbeite, während Skript A noch angehalten ist, kann es nach dem Speichern der Änderungen fortgesetzt werden. Diese Änderungen wirken sich auch auf die Ausführung des Codes aus.
Hier ist die eigentliche Frage: Gibt es eine Möglichkeit, Skript A zu bearbeiten, während es noch ausgeführt wird? Oder ist eine Bearbeitung nach Beginn der Ausführung nicht mehr möglich?
quelle
Antworten:
In Unix erstellen die meisten Editoren eine neue temporäre Datei mit den bearbeiteten Inhalten. Beim Speichern der bearbeiteten Datei wird die Originaldatei gelöscht und die temporäre Datei in den Originalnamen umbenannt. (Es gibt natürlich verschiedene Sicherheitsvorkehrungen, um Datenverlust zu verhindern.) Dies ist beispielsweise der Stil, der von
sed
oderperl
beim Aufrufen mit dem-i
Flag ("an Ort und Stelle") verwendet wird, das überhaupt nicht wirklich "an Ort und Stelle" ist. Es hätte "neuer Ort mit altem Namen" heißen sollen.Dies funktioniert gut, da Unix (zumindest für lokale Dateisysteme) sicherstellt, dass eine geöffnete Datei weiterhin vorhanden ist, bis sie geschlossen wird, auch wenn sie "gelöscht" und eine neue Datei mit demselben Namen erstellt wird. (Es ist kein Zufall, dass der Unix-Systemaufruf zum "Löschen" einer Datei tatsächlich als "Verknüpfung aufheben" bezeichnet wird.) Wenn also in einem Shell-Interpreter eine Quelldatei geöffnet ist und Sie die Datei auf die oben beschriebene Weise "bearbeiten" sieht die Shell die Änderungen nicht einmal, da die Originaldatei noch geöffnet ist.
[Hinweis: Wie bei allen auf Standards basierenden Kommentaren unterliegt das Obige mehreren Interpretationen, und es gibt verschiedene Eckfälle, wie z. B. NFS. Pedanten sind herzlich eingeladen, die Kommentare mit Ausnahmen zu füllen.]
Es ist natürlich möglich, Dateien direkt zu ändern. Dies ist für Bearbeitungszwecke nicht besonders praktisch, da Sie zwar Daten in einer Datei überschreiben können, diese jedoch nicht löschen oder einfügen können, ohne alle folgenden Daten zu verschieben, was eine erhebliche Umschreibung zur Folge hätte. Außerdem wäre der Inhalt der Datei während dieser Verschiebung unvorhersehbar, und Prozesse, bei denen die Datei geöffnet war, würden darunter leiden. Um damit durchzukommen (wie zum Beispiel bei Datenbanksystemen), benötigen Sie einen ausgeklügelten Satz von Änderungsprotokollen und verteilten Sperren. Dinge, die weit über den Rahmen eines typischen Dateibearbeitungsprogramms hinausgehen.
Wenn Sie also eine Datei bearbeiten möchten, während sie von einer Shell verarbeitet wird, haben Sie zwei Möglichkeiten:
Sie können an die Datei anhängen. Das sollte immer funktionieren.
Sie können die Datei mit neuen Inhalten genau gleicher Länge überschreiben . Dies kann funktionieren oder auch nicht, je nachdem, ob die Shell diesen Teil der Datei bereits gelesen hat oder nicht. Da die meisten Datei-E / A-Vorgänge Lesepuffer beinhalten und alle mir bekannten Shells einen gesamten zusammengesetzten Befehl lesen, bevor sie ausgeführt werden, ist es ziemlich unwahrscheinlich, dass Sie damit durchkommen. Es wäre sicherlich nicht zuverlässig.
Ich kenne keine Formulierung im Posix-Standard, die tatsächlich das Anhängen einer Skriptdatei erfordert, während die Datei ausgeführt wird. Daher funktioniert sie möglicherweise nicht mit jeder Posix-kompatiblen Shell, geschweige denn mit dem aktuellen Angebot von fast und manchmal posix-konforme Schalen. Also YMMV. Aber meines Wissens funktioniert es zuverlässig mit bash.
Als Beweis ist hier eine "schleifenfreie" Implementierung des berüchtigten 99-Flaschen-Bier-Programms in bash, das
dd
zum Überschreiben und Anhängen verwendet wird (das Überschreiben ist vermutlich sicher, da es die aktuell ausgeführte Zeile ersetzt, die immer die letzte Zeile der Datei, mit einem Kommentar von genau der gleichen Länge; ich habe das getan, damit das Endergebnis ohne das selbstmodifizierende Verhalten ausgeführt werden kann.)quelle
export beer=100
vor dem Ausführen des Skripts mache , funktioniert es wie erwartet.bash
stellt sicher, dass Befehle gelesen werden, bevor sie ausgeführt werden.Zum Beispiel in:
Die Shell liest das Skript blockweise. Lesen Sie also wahrscheinlich beide Befehle, interpretieren Sie den ersten und gehen Sie dann zum Ende des
cmd1
Skripts zurück und lesen Sie das Skript erneut,cmd2
um es zu lesen und auszuführen.Sie können es leicht überprüfen:
(Wenn man sich die
strace
Ausgabe darüber ansieht , scheint es, als ob sie ausgefallenere Dinge tut (wie das mehrfache Lesen der Daten, das Zurücksuchen ...), als wenn ich es vor ein paar Jahren versucht hätte, also könnte meine obige Aussage über das Zurücksuchen zurück sein gilt nicht mehr für neuere Versionen).Wenn Sie jedoch Ihr Skript schreiben als:
Die Shell muss bis zum Abschluss lesen
}
, speichern und ausführen. Aus diesem Grundexit
liest die Shell das Skript nicht erneut, sodass Sie es sicher bearbeiten können, während die Shell es interpretiert.Stellen Sie alternativ beim Bearbeiten des Skripts sicher, dass Sie eine neue Kopie des Skripts schreiben. Die Shell liest weiterhin das Original (auch wenn es gelöscht oder umbenannt wurde).
Um dies zu tun, benennen Sie es
the-script
um,the-script.old
kopieren Siethe-script.old
esthe-script
und bearbeiten Sie es.quelle
Es gibt wirklich keine sichere Möglichkeit, das Skript während der Ausführung zu ändern, da die Shell das Puffern zum Lesen der Datei verwenden kann. Wenn das Skript durch Ersetzen durch eine neue Datei geändert wird, liest Shells die neue Datei in der Regel erst, nachdem bestimmte Vorgänge ausgeführt wurden.
Wenn ein Skript während der Ausführung geändert wird, meldet die Shell häufig Syntaxfehler. Dies liegt an der Tatsache, dass die Shell beim Schließen und erneuten Öffnen der Skriptdatei den Byte-Offset in der Datei verwendet, um sich bei der Rückkehr neu zu positionieren.
quelle
Sie können dies umgehen, indem Sie eine Falle in Ihrem Skript setzen und dann
exec
die neuen Skriptinhalte mit abrufen. Beachten Sie jedoch, dass derexec
Aufruf das Skript von Grund auf neu startet und nicht an der Stelle, an der es im laufenden Prozess angekommen ist. Daher wird Skript B aufgerufen (usw.).Dadurch wird das Datum weiterhin auf dem Bildschirm angezeigt. Ich kann dann meinen Skript und Änderung bearbeiten
date
zuecho "Date: $(date)"
. Beim Ausschreiben des Skripts wird immer noch nur das Datum angezeigt. Wenn ich jedoch das Signal sende, das ich für dietrap
Erfassung festgelegt habe, ersetzt das Skriptexec
(den aktuell ausgeführten Prozess durch den angegebenen Befehl) den Befehl$CMD
und die Argumente$@
. Sie können dies tun, indem Siekill -1 PID
- wobei PID die PID des ausgeführten Skripts ist - eingeben und die Ausgabe so ändern, dass sieDate:
vor derdate
Befehlsausgabe angezeigt wird.Sie können den "Status" Ihres Skripts in einer externen Datei (in "say / tmp") speichern und den Inhalt lesen, um zu erfahren, wo er beim erneuten Ausführen des Programms "fortgesetzt" werden soll. Sie können dann eine zusätzliche Trap-Beendigung (SIGINT / SIGQUIT / SIGKILL / SIGTERM) hinzufügen, um diese tmp-Datei zu löschen. Wenn Sie also nach dem Unterbrechen von "Skript A" einen Neustart durchführen, beginnt sie von vorne. Eine zustandsbehaftete Version wäre so etwas wie:
quelle
$0
und$@
zu Beginn des Skripts diese Variablen inexec
verwendet habe.