Sourcing eines Bash-Skripts - Return on Error statt Exit?

15

Ich verwende ein Bash-Skript im Terminal und beende es bei einem Fehler mit

set -o errexit

bricht mein Terminal ab, was EXTREM ÄRGERLICH ist, weil ich das Terminal schließen, ein anderes öffnen und einige Variablen zurücksetzen muss.

Bisher mit

command || return

lines macht im script genau das, was ich will

set -o errexit

zu tun ... Aber ich möchte, dass es für das gesamte Drehbuch getan wird; nicht nur eine Zeile / Befehl

Ich habe eine Datei mit Befehlen zum Einrichten einer Site, und ich möchte lieber keinen Befehl || ausführen Rückkehr

für jede einzelne Zeile in der Datei

Gibt es eine andere festgelegte Option oder etwas anderes, das einfach "zurückkehrt", anstatt das Terminal zu verlassen?

- Aus Gründen der Übersichtlichkeit möchte ich das Skript beenden und das Terminal in dem Zustand belassen, in dem das Drücken von Strg + C zum Beenden eines im Terminal ausgeführten Dienstes ausgeführt wird. command || returntut das. Aber ich möchte nicht auf || returnjede Zeile in der Datei eingehen. Ich suche also nach etwas ähnlichem set -o errexit, das nicht dazu führt, dass das Terminal heruntergefahren wird

--- Hinweis: Erstellen Sie ein dummes Skript mit zwei Zeilen (super.sh):

create_path=~/Desktop/site_builder/create.sh
source $create_path blah

Und set -o errexitan der Spitze von create.sh platzieren,

funktioniert genau so wie ich es erwarte. Es ist jedoch wirklich dumm, eine Datei mit zwei Zeilen darin zu erstellen, nur um ein anderes Bash-Skript aufzurufen, anstatt es nur vom Terminal aus aufzurufen. Ugghhh

Hier sind einige Beispiele:

in super.sh

#!/bin/bash

create_path=~/Desktop/site_builder/create.sh
source $create_path blah

in create.sh

#!/bin/bash
set -o errexit
#line below this is a line that fails and will cause the script to stop and return to the terminal as expected 
sed "s/@@SITE_NAME@@/$dirname" 
~/Desktop/site_builder/template_files/base.html > ~/Desktop/$dirname/templates/base.html # a line with a stupid error

im terminal:

 $ bash super.sh

Ausgabe wie erwartet:

my-mac$

Das funktioniert. Was für eine nervige Lösung.

Ich möchte im Idealfall ausführen, was in der dummen Datei super.sh vom Terminal ist, nicht in der Datei super.sh: D, ohne dass das Terminal mich herunterfährt. Das passiert mit dem, was ich versuche:

Terminalbefehl:

my-mac$ source $create_path blah

in create.sh habe ich noch set -o errexit

Hier ist die Ausgabe auf dem Terminal

    sed: 1: "s/@@SITE_NAME@@/blah": unterminated substitute in regular expression
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

Und dann ist das Terminal eingefroren. Strg + C funktioniert nicht, ebenso wenig wie Strg + D

Wenn set -o errexitich stattdessen nur command || returnAnweisungen überall in der Datei create.sh verwende, erhalte ich genau das, was ich will, während ich die Zeilen in supser.sh direkt auf dem Terminal ausführe (anstatt super.sh vom Terminal aus aufzurufen). Aber das ist auch keine praktische Lösung.

Hinweis: Ich mochte die Antwort von @terdon, dass ich nur eine untergeordnete Shell erzeugt habe, und habe am Ende nur eine untergeordnete Shell über das Skript anstelle des Terminals erzeugt, wie er in seiner Antwort mit geschweiften Klammern ( )um das gesamte Skript herum gezeigt hat funktioniert auch.


quelle
Tun Sie dies in einem Skript oder manuell?
Terdon
Ich rufe die Datei mit der Quelle im Terminal auf. Wie in: source $file_path argumentDas Skript wird in derselben Shell ausgeführt, von der ich es aufgerufen habe (was sourcemir gesagt wurde ... und es verhält sich so). command || returnAnweisungen befinden sich in der Datei, die ich im Terminal ausführe
OK, und wo läuft das Set? Befindet sich das im Quellenskript oder führen Sie es manuell aus? Dies ist viel einfacher zu beantworten, wenn Sie ein einfaches Beispiel hinzufügen, das wir kopieren und ausprobieren können. Ich habe eine Idee, aber ich brauche eine Möglichkeit zum Testen, um sicherzugehen, dass es funktioniert.
Terdon
Ich habe dem ursprünglichen Beitrag einen Hinweis hinzugefügt, der dabei helfen könnte. Aber ich werde auch tun, was Sie vorgeschlagen haben.

Antworten:

4

Quell die Datei einfach mit einem Failsafe:

source the-source-file || true

... dann wird der Gesamtbefehl nicht fehlschlagen, auch wenn dies der sourceFall ist.

Jeff Schaller
quelle
Eigentlich dachte ich, dass dies das war, was ich wollte, aber es stellte sich heraus, dass mein Terminal nur langsam war ... Ich möchte eigentlich bei einem Fehler zum Terminal zurückkehren (was bedeutet, dass das gesamte Skript in seinen Spuren angehalten wird) ... aus welchem ​​Grund auch immer, source file || true tut dies nicht und auch nicht, source file || return wenn ich dies in das Terminal
Sourcing der Datei bringt Sie nicht zu einer Shell-Eingabeaufforderung zurück?
Jeff Schaller
Dies ist, was in meinem Terminal ist: Jills-MBP:~ jillr$ source ~/Desktop/site_builder/create.sh blah || trueEs führt einfach den nächsten Teil des Skripts bei einem Fehler aus, also kehrt es zurück anstatt wahr. Das einzige, was funktioniert hat, sind command || returnAnweisungen in der eigentlichen Datei für alle Befehle, was albern ist. Ich weiß nicht, ob es auch etwas nützen würde, wenn man alles in eine Funktion wirft und function || returnam Ende der Datei aufruft .
Wenn Sie explizit || returneinen Befehl eingeben, der fehlschlägt, wird das Ja zurückgegeben. Ich dachte, wir versuchen, Ihre Haupt- / Eltern-Shell am Beenden zu hindern?
Jeff Schaller
Ja, ich möchte verhindern, dass das Terminal tatsächlich beendet wird. Weil ich das Terminal nicht verlassen will. Ich möchte das Skript beenden. Bei der Rückkehr zu einzelnen Befehlen im Skript wird das Skript beendet und mein Terminal in demselben Zustand belassen, wie wenn Sie Strg + C drücken, um einen Prozess abzubrechen, z. B. den Server vom Terminal aus auszuführen. Ich möchte das Schreiben von Befehlen vermeiden || Rückkehr für jede Zeile in der Datei: D
2

Dies ist das einzige, was für das, was ich tun musste, funktioniert (Erstellen einer virtuellen Umgebung, Aktivieren und Installieren der Anforderungen mithilfe eines Bash-Skripts):

Erzeugen einer Subshell / Child-Shell aus dem Skript, wie in:

stupid_file.sh

(
set -o errexit
#bunch of commands
#one line fails
)

führe die stupid_file aus mit:

source stupid_file.sh <file arguments here> || true

DAS ENDE.

** Verbeugt sich **

(Dank geht an Jeff und Terdon)


quelle
1
Dies ist eigentlich nichts anderes, als nur das Skript auszuführen, anstatt es auszuliefern.
Chepner
@chepner hmmm. Cool. Ich muss versuchen, einfach anzurufen bash $create_path blahund zu sehen, ob es immer noch beendet wird, auf die gleiche Weise ausgeführt wird und weiterhin alles korrekt in meine virtuelle Umgebung installiert. Keine Zeit mehr.
Ich werde Ihnen die Zeit sparen: Es wird nicht (wenn Sie mit "Sachen installieren" Umgebungsvariablen in Ihrer aktuellen Shell festlegen) und auch nicht (...), da alle Zuweisungen in Klammern nur diese Subshell betreffen. foo=3; (foo=5); echo "$foo"wird 3, nicht 5
ausgeben
@chepner nicht genau. Ich kann nicht source filevom Terminal aus anrufen und die gleichen Ergebnisse erwarten. Weil das nie geklappt hat. Das einzige Mal, dass ich die gleichen Ergebnisse erhalte, ist das explizite Erstellen einer Sub-Shell, ob über das Terminal oder über das Skript. Ich kann jedoch bashanstelle von verwenden source, um das Skript auszuführen und die gleichen Ergebnisse zu erhalten, aber nur, wenn ich mein Skript in einer Sub-Shell explizit ausführe
@chepner durch Installation von Dingen, ich meine eigentlich eine virtuelle Umgebung erstellen, diese virtuelle Umgebung aktivieren und dann pip install -r $apath/requirements.txtin dieser aktivierten Umgebung ausführen . Es ist , warum ich Quelle in erster Linie verwendet , um das Skript aufrufen
0

Als einfache Umgehung können Sie eine Shell in Ihrer aktuellen Shell und Quelle ausführen. Etwas wie:

  1. Öffnen Sie ein neues Terminal und richten Sie alles nach Ihren Wünschen ein. Sie haben einige Umgebungsvariablen und dergleichen erwähnt. Stellen Sie sie hier auf.

  2. Starten Sie in diesem Terminal eine neue Shell. Zum Beispiel bash.

  3. Mach dein Ding. Geben Sie Ihr Skript als Quelle ein. Wenn es beendet wird, werden Sie einfach in die erste Shell geworfen und alles ist noch eingerichtet. Laufen bashSie einfach noch einmal und Sie sind wieder im Geschäft.

Zur Veranschaulichung habe ich dieses Skript erstellt, das fehlschlägt, wenn Sie versuchen, es als Quelle zu verwenden:

$ cat /home/terdon/scripts/bar.sh
set -o errexit
var='bar

Mal sehen, was passiert, wenn ich eine verschachtelte Shell-Sitzung starte und sie dann als Quelle benutze (beachte, dass ich den portablen Namen für den sourceBefehl verwende .; sourceist ein Bashismus):

parent-shell $ bash      ## start a new shell
child-shell $ . ~/scripts/bar.sh
bash: /home/terdon/scripts/bar.sh: line 2: unexpected EOF while looking for matching `''
parent-shell $ 

Wie Sie sehen, führte der Syntaxfehler zum Beenden des Quell-Skripts, was wiederum dazu führte, dass meine Shell-Sitzung beendet wurde. Da es sich jedoch um eine verschachtelte Sitzung handelte, landete ich gerade wieder in der ursprünglichen übergeordneten Shell, in der alle Variablen noch eingerichtet waren . Führen Sie jetzt einfach erneut eine neue Shell aus, und Sie können zur Suche nach Ihrem Skript zurückkehren.

terdon
quelle
Ich werde das ganz schnell
Dies hat den beabsichtigten Effekt ... Der Nachteil ist, dass die Variablen, die ich für die übergeordnete Shell erstelle, in der untergeordneten Shell nicht verfügbar sind. Diese enthält eine der Variablen, die ich für die Ausführung in der untergeordneten Shell benötige. Gibt es eine Möglichkeit, eine Subshell aus der von mir ausgeführten Datei zu erzeugen? Auf diese Weise kann ich das Skript weiterhin in der übergeordneten Shell aufrufen und auf die benötigten Variablen zugreifen. Ich setze buchstäblich create_path=~/Desktop/site_builder/create.sh in die übergeordnete Shell, weil ich das so oft mache. Und ich brauche es, um die Quelle $ create_path [Argument] aufzurufen, um das Skript auszuführen.
1
@JillRussek Wenn die Variablen nicht an die untergeordnete Shell übergeben werden, werden sie nicht verwendet export. Aber was Sie beschreiben, macht wirklich wenig Sinn. Es klingt immer mehr nach einem XY-Problem . Vielleicht möchten Sie eine neue Frage stellen, in der erläutert wird, was Ihr Endziel ist. Ich wette, wir können Ihnen eine bessere Lösung für all diese seltsamen Beschaffungen anbieten.
Terdon
Hmm. Ich könnte das auch versuchen. Im Moment ist es genau das, was ich will, wenn ich die Child-Shell nicht vom Terminal, sondern vom Skript aus spawne. Ich muss das Skript über das Terminal beziehen, um eine virtuelle Umgebung im Skript zu erstellen und Dinge darin zu installieren. Es funktioniert jetzt wie vorgesehen.
Aber Sie haben Recht, ich habe die Variablen nicht exportiert, weil ich nicht wusste, dass ich Folgendes tun muss: D. (Ich habe noch nie eine Kinderschale erzeugt)