Bearbeiten Sie das Shell-Skript, während es ausgeführt wird

91

Können Sie ein Shell-Skript während der Ausführung bearbeiten und die Änderungen wirken sich auf das ausgeführte Skript aus?

Ich bin neugierig auf den speziellen Fall eines csh-Skripts, bei dem Batch eine Reihe verschiedener Build-Varianten ausführt und die ganze Nacht ausgeführt wird. Wenn mir während der Operation etwas einfällt, möchte ich zusätzliche Befehle hinzufügen oder nicht ausgeführte auskommentieren.

Wenn nicht möglich, gibt es eine Shell oder einen Batch-Mechanismus, der mir dies ermöglicht?

Natürlich habe ich es versucht, aber es wird Stunden dauern, bis ich sehe, ob es funktioniert hat oder nicht, und ich bin gespannt, was hinter den Kulissen passiert oder nicht.

ack
quelle
1
Ich habe zwei Ergebnisse beim Bearbeiten der Skriptdatei für ein laufendes Skript gesehen: 1) Die Änderungen werden ignoriert, als hätte sie das Ganze in den Speicher gelesen, oder 2) das Skript stürzt mit einem Fehler ab, als hätte es einen Teil des Befehls gelesen. Ich weiß nicht, ob das von der Größe des Skripts abhängt. In jedem Fall würde ich es nicht versuchen.
Paul Tomblin
Kurz gesagt: Nein, es sei denn, es ist selbstreferenziell / aufrufend. In diesem Fall wäre das Hauptskript immer noch das alte.
Wrikken
Hier gibt es zwei wichtige Fragen. 1) Wie kann ich einem laufenden Skript Befehle korrekt und sicher hinzufügen? 2) Was passiert, wenn ich ein laufendes Skript ändere?
Chris Quenelle
3
Die Frage ist, ob eine Shell ein Skript ausführt, indem sie die gesamte Skriptdatei liest und dann ausführt oder indem sie es während der Ausführung teilweise liest. Ich weiß nicht was es ist; es kann nicht einmal angegeben werden. Sie sollten je nach Verhalten vermeiden.
Keith Thompson

Antworten:

-67

Skripte funktionieren nicht so. Die ausführende Kopie ist unabhängig von der Quelldatei, die Sie bearbeiten. Wenn das Skript das nächste Mal ausgeführt wird, basiert es auf der zuletzt gespeicherten Version der Quelldatei.

Es kann ratsam sein, dieses Skript in mehrere Dateien aufzuteilen und einzeln auszuführen. Dies reduziert die Ausführungszeit bis zum Ausfall. (dh teilen Sie den Stapel in ein Build-Flavour-Skript auf und führen Sie jedes einzeln aus, um festzustellen, welches die Probleme verursacht).

Ford
quelle
66
Ich habe das Gegenteil beobachtet. Das Ausführen von Bash-Skripten, die bearbeitet werden, kann zum Absturz des ausgeführten Skripts führen, da sich die Datei anscheinend unter die Position der Bash-Skriptlesedatei bewegt.
Tilman Vogel
10
Nach meiner Erfahrung auf mehreren Systemen ist die ausführende Kopie NICHT unabhängig von der Datenträgerdatei. Deshalb ist dieses Problem bei der Programmierung von Shell-Skripten so überraschend und wichtig.
Chris Quenelle
6
Es ist definitiv nicht unabhängig von der On-Disc-Datei. Die Shell liest die Skripte normalerweise nur in Blöcken von beispielsweise 128 Bytes oder 4096 Bytes oder 16384 Bytes und liest den nächsten Block nur, wenn neue Eingaben erforderlich sind. (Sie können Dinge wie lsof auf einer Shell tun, auf der ein Skript ausgeführt wird, und sehen, dass die Datei noch geöffnet ist.)
mirabilos
5
Nein. Wenn Sie ein Skript bearbeiten, schlägt der Prozess tatsächlich fehl.
Erik Aronesty
8
Du bist nicht richtig. Es wird abhängig von der Implementierung und dem tatsächlichen Befehl, der im Skript aufgerufen wird, gepuffert. Ob stdout in eine Datei umgeleitet wird, es gibt viele Faktoren und Ihre Antwort ist nicht einfach richtig.
GL2014
50

Es wirkt sich zumindest auf meine Umgebung aus, aber auf sehr unangenehme Weise . Siehe diese Codes. Erstens a.sh:

#!/bin/sh

echo "First echo"
read y

echo "$y"

echo "That's all."

b.sh::

#!/bin/sh

echo "First echo"
read y

echo "Inserted"

echo "$y"

# echo "That's all."

Machen

$ cp a.sh run.sh
$ ./run.sh
$ # open another terminal
$ cp b.sh run.sh  # while 'read' is in effect
$ # Then type "hello."

In meinem Fall ist die Ausgabe immer:

Hallo
Hallo
Das ist alles.
Das ist alles.

(Natürlich ist es viel besser, es zu automatisieren, aber das obige Beispiel ist lesbar.)

[bearbeiten] Dies ist unvorhersehbar und daher gefährlich. Die beste Problemumgehung besteht darin , wie hier beschrieben , alle in eine Klammer zu setzen und vor der schließenden Klammer "exit" zu setzen . Lesen Sie die verknüpfte Antwort gut durch , um Fallstricke zu vermeiden.

[hinzugefügt] Das genaue Verhalten hängt von einem zusätzlichen Zeilenumbruch ab und möglicherweise auch von Ihrem Unix-Geschmack, Dateisystem usw. Wenn Sie nur einige Einflüsse sehen möchten, fügen Sie b.sh einfach vorher und / oder nachher "echo foo / bar" hinzu die "gelesene" Zeile.

teika kazura
quelle
3
Mh, ich sehe die Zuneigung nicht. Vermisse ich etwas
Benutzer unbekannt
Das genaue Verhalten hängt von einem zusätzlichen Zeilenumbruch ab , und möglicherweise auch von der Unix- Version , dem Dateisystem usw., die bei al. Wenn Sie einfach nur Einflüsse sehen möchten, vergrößern Sie diese einfach, b.shindem Sie 10 Zeilen Echo foo / bar / baz hinzufügen. Der Kern der Antworten von dave4220 und mir ist, dass der Effekt nicht leicht vorherzusagen ist. (Übrigens bedeutet das Substantiv "Zuneigung" "Liebe" =)
teika kazura
Ja, es ist sehr kaputt. Ich habe eine Lösung (unten). Was noch gefährlicher ist, sind svn / rsync / git-Updates
Erik Aronesty
39

Versuchen Sie dies ... erstellen Sie eine Datei mit dem Namen bash-is-odd.sh:

#!/bin/bash
echo "echo yes i do odd things" >> bash-is-odd.sh

Das zeigt, dass Bash tatsächlich das Skript "wie du gehst" interpretiert. In der Tat führt das Bearbeiten eines Skripts mit langer Laufzeit zu unvorhersehbaren Ergebnissen, dem Einfügen zufälliger Zeichen usw. Warum? Da Bash von der letzten Byteposition liest, verschiebt das Bearbeiten die Position des aktuell gelesenen Zeichens.

Bash ist mit einem Wort aufgrund dieser "Funktion" sehr, sehr unsicher. svn und die rsyncVerwendung mit Bash-Skripten sind besonders problematisch, da sie standardmäßig die Ergebnisse "zusammenführen" ... an Ort und Stelle bearbeiten. rsynchat einen Modus, der dies behebt. svn und git nicht.

Ich präsentiere eine Lösung. Erstellen Sie eine Datei mit dem Namen /bin/bashx:

#!/bin/bash
source "$1"

Verwenden #!/bin/bashxSie jetzt Ihre Skripte und führen Sie sie immer mit bashxstatt aus bash. Dies behebt das Problem - Sie können rsyncIhre Skripte sicher speichern.

Alternative (Inline-) Lösung, vorgeschlagen / getestet von @ AF7:

{
   # your script
} 
exit $?

Geschweifte Klammern schützen vor Änderungen und Exit schützt vor Anhängen. Natürlich wären wir alle viel besser dran, wenn bash mit einer Option wie -w(ganze Datei) oder etwas, das dies tat, kommen würde.

Erik Aronesty
quelle
1
Übrigens; Hier ist ein Plus, um dem Minus entgegenzuwirken, und weil mir Ihre bearbeitete Antwort gefällt.
Andrew Barber
1
Ich kann das nicht empfehlen. In dieser Problemumgehung werden Positionsparameter um eins verschoben. Denken Sie auch daran, dass Sie $ 0 keinen Wert zuweisen können. Wenn Sie einfach "/ bin / bash" in "/ bin / bashx" ändern, schlagen viele Skripte fehl.
Teika Kazura
1
Bitte sagen Sie mir, dass eine solche Option bereits implementiert wurde!
AF7
10
Eine einfache Lösung, die mir von meinem Freund Giulio vorgeschlagen wurde (Credits, wo fällig), besteht darin, {am Anfang und} am Ende des Scritps einzufügen. Bash ist gezwungen, alles im Speicher zu lesen.
AF7
1
@ AF7 Verbesserung der Lösung Ihres Freundes: {your_code; } && Ausfahrt; verhindert, dass an das Ende angehängte Zeilen ebenfalls ausgeführt werden.
Corkman
17

Teilen Sie Ihr Skript in Funktionen auf, und jedes Mal, wenn eine Funktion aufgerufen wird, wird sourcees aus einer separaten Datei aufgerufen . Dann können Sie die Dateien jederzeit bearbeiten und Ihr laufendes Skript übernimmt die Änderungen, wenn es das nächste Mal bezogen wird.

foo() {
  source foo.sh
}
foo
Glenn Jackman
quelle
Ich verwende diese Technik seit einiger Zeit effektiv, um meine lang laufenden Build-Skripte zu aktualisieren, während sie ausgeführt werden. Ich würde gerne eine Technik lernen, mit der die aktuelle Datei bis zum Ende der Datei gelesen wird, sodass ich nicht zwei Dateien haben muss, um jedes Shell-Skript zu implementieren.
Chris Quenelle
3

Gute Frage! Hoffe, dieses einfache Skript hilft

#!/bin/sh
echo "Waiting..."
echo "echo \"Success! Edits to a .sh while it executes do affect the executing script! I added this line to myself during execution\"  " >> ${0}
sleep 5
echo "When I was run, this was the last line"

Es scheint unter Linux, dass Änderungen, die an einer ausführenden .sh vorgenommen werden, vom ausführenden Skript vorgenommen werden, wenn Sie schnell genug tippen können!

Will Turner
quelle
2

Eine interessante Randnotiz: Wenn Sie ein Python-Skript ausführen, ändert sich dies nicht. (Dies ist wahrscheinlich für jeden offensichtlich, der versteht, wie Shell Python-Skripte ausführt, aber dachte, es könnte eine nützliche Erinnerung für jemanden sein, der nach dieser Funktionalität sucht.)

Ich erschuf:

#!/usr/bin/env python3
import time
print('Starts')
time.sleep(10)
print('Finishes unchanged')

Bearbeiten Sie dann in einer anderen Shell, während diese im Ruhezustand ist, die letzte Zeile. Wenn dies abgeschlossen ist, wird die unveränderte Zeile angezeigt, vermutlich weil ein .pyc? Gleiches passiert unter Ubuntu und macOS.

Chris
quelle
1

Ich habe csh nicht installiert, aber

#!/bin/sh
echo Waiting...
sleep 60
echo Change didn't happen

Führen Sie das aus und bearbeiten Sie schnell die letzte zu lesende Zeile

echo Change happened

Ausgabe ist

Waiting...
/home/dave/tmp/change.sh: 4: Syntax error: Unterminated quoted string

Hrmph.

Ich denke, Änderungen an den Shell-Skripten werden erst wirksam, wenn sie erneut ausgeführt werden.

dave4420
quelle
2
Sie sollten die Zeichenfolge, die Sie anzeigen möchten, in Anführungszeichen setzen.
user1463308
2
Tatsächlich zeigt es, dass Ihr Editor nicht so funktioniert, wie Sie denken. Viele, viele Editoren (einschließlich vim, emacs) arbeiten mit einer "tmp" -Datei und nicht mit der Live-Datei. Versuchen Sie, "echo 'echo uh oh' >> myshell.sh" anstelle von vi / emacs zu verwenden ... und beobachten Sie, wie das neue Material ausgegeben wird. Schlimmer noch ... svn und rsync bearbeiten auch so!
Erik Aronesty
3
-1. Dieser Fehler hängt nicht mit der zu bearbeitenden Datei zusammen: Sie verwenden ein Apostroph! Das wirkt wie ein einfaches Anführungszeichen und verursacht den Fehler. Setzen Sie die ganze Zeichenfolge in doppelte Anführungszeichen und versuchen Sie es erneut.
Anonymer Pinguin
5
Die Tatsache, dass der Fehler aufgetreten ist, zeigt, dass die Bearbeitung nicht den beabsichtigten Effekt hatte.
Danmcardle
@danmcardle Wer weiß? Vielleicht Bash Saw Change didn'ned.
Kirill Bulygin
1

Wenn dies alles in einem einzigen Skript enthalten ist, funktioniert es nicht. Wenn Sie es jedoch als Treiberskript einrichten, das Unterskripte aufruft, können Sie möglicherweise ein Unterskript ändern, bevor es aufgerufen wird, oder bevor es erneut aufgerufen wird, wenn Sie eine Schleife ausführen. In diesem Fall glaube ich, dass sich diese Änderungen ändern würde sich in der Ausführung widerspiegeln.

Ethan Shepherd
quelle
0

Ich höre nein ... aber was ist mit einer Indirektion:

BatchRunner.sh

Command1.sh
Command2.sh

Command1.sh

runSomething

Command2.sh

runSomethingElse

Dann sollten Sie in der Lage sein, den Inhalt jeder Befehlsdatei zu bearbeiten, bevor BatchRunner dazu kommt, oder?

ODER

Bei einer saubereren Version würde BatchRunner auf eine einzelne Datei schauen, in der jeweils eine Zeile nach der anderen ausgeführt wird. Dann sollten Sie in der Lage sein, diese zweite Datei zu bearbeiten, während die erste richtig läuft?

ack
quelle
Ich frage mich, ob es sie in den Speicher lädt, um sie auszuführen, und eine Änderung spielt keine Rolle, sobald der Hauptprozess eingeleitet wird ...
Eric Hodonsky
0

Verwenden Sie stattdessen Zsh für Ihre Skripterstellung.

AFAICT, Zsh zeigt dieses frustrierende Verhalten nicht.

Micah Elliott
quelle
Dies ist Grund # 473, Zsh dem Bash vorzuziehen. Ich habe kürzlich an einem alten Bash-Skript gearbeitet, dessen Ausführung 10 m dauert, und ich kann es nicht bearbeiten, während ich darauf warte, dass es abgeschlossen ist!
Micah Elliott
-5

Normalerweise ist es ungewöhnlich, dass Sie Ihr Skript während der Ausführung bearbeiten. Alles, was Sie tun müssen, ist, die Kontrolle über Ihre Operationen zu überprüfen. Verwenden Sie if / else-Anweisungen, um nach Bedingungen zu suchen. Wenn etwas fehlschlägt, dann mach das, sonst mach das. Das ist der richtige Weg.

Ghostdog74
quelle
Es geht weniger darum, dass Skripte fehlschlagen, als vielmehr darum, den Stapeljob während des Vorgangs zu ändern. IE erkennt, dass ich mehr kompilieren möchte oder dass ich bestimmte Jobs nicht bereits in der Warteschlange benötige.
Bestätigen Sie den
1
Wenn Sie sich strikt an Skripte anhängen , wird bash das tun, was Sie erwarten!
Erik Aronesty