Rückgabe bei Fehler in Shellscript statt Beenden bei Fehler

7

Ich weiß, dass set -edas mein Freund ist, um bei einem Fehler zu beenden. Aber was tun, wenn das Skript bezogen wird, z. B. wenn eine Funktion über die Konsole ausgeführt wird? Ich möchte die Konsole nicht bei einem Fehler schließen, sondern nur das Skript stoppen und die Fehlermeldung anzeigen.

Muss ich die $ überprüfen? von jedem Befehl von Hand, um dies zu ermöglichen?

Hier ein Beispielskript myScript.sh, um das Problem zu zeigen:

#!/bin/sh
set -e

copySomeStuff()
{
    source="$1"
    dest="$2"
    cp -rt "$source" "$dest"
    return 0
}

installStuff()
{
    dest="$1"
    copySomeStuff dir1 "$dest"
    copySomeStuff dir2 "$dest"
    copySomeStuff nonExistingDirectory "$dest"
}

Das Skript wird folgendermaßen verwendet:

$ source myScript.sh
$ installStuff

Dadurch wird nur die Konsole geschlossen. Der von angezeigte Fehler cpgeht verloren.

Alex
quelle
1
Sind Sie sicher, dass die Konsole dadurch geschlossen wird? Ich habe versucht, das Skript so auszuführen, wie Sie es beschrieben haben, und es beendet die gesamten cpFehler beim Beenden und Beenden mit 1 Exit-Code.
ddnomad
1
@ddnomad Hiermit wird set -edie errexitOption in der Shell festgelegt. Beim Aufrufen der Funktion cpschlägt die fehl und die Shell wird für mich beendet.
Kusalananda
1
Wenn Sie angeben, dass es das ignoriert #!und set -e auf die aufrufende Shell angewendet wird, gilt dies auch für alle nachfolgenden Befehle.
Strg-Alt-Delor
Zitieren Sie Ihre Benutzereingaben doppelt. Nicht verwenden, ()wenn Shell-Funktionen aufgerufen werden.
Kusalananda
${dest}und ${source}sind immer noch nicht zitiert und enthalten Benutzereingaben. Ich bin hier etwas wählerisch mit Ihnen, aber Sie müssen sie in Ihrem Skript auf Ihrem Computer zitieren, sonst funktionieren einfache Dinge wie das Kopieren von Dateien mit Leerzeichen in ihren Namen nicht. Es tut uns leid. Ich versuche nur, konstruktives Feedback zu geben.
Kusalananda

Antworten:

5

Ich würde empfehlen, ein Skript zu haben, das Sie als Sub-Shell ausführen und möglicherweise eine Datei zum Einlesen von Funktionsdefinitionen beziehen. Lassen Sie dieses Skript die errexitShell-Option für sich selbst festlegen .

Wenn Sie sourceüber die Befehlszeile verwenden, ist "das Skript" effektiv Ihre interaktive Shell. Beenden bedeutet, die Shell-Sitzung zu beenden. Es gibt möglicherweise Möglichkeiten, dies zu umgehen, aber die beste Option, wenn Sie errexiteine Sitzung festlegen möchten, besteht darin, einfach Folgendes zu haben:

#!/bin/bash

set -o errexit

source file_with_functions
do_things_using_functions

Zusätzlicher Vorteil: Verschmutzt die interaktive Sitzung nicht mit Funktionen.

Kusalananda
quelle
Ok, das macht den Job, danke! Es ist eine besser lesbare Lösung und sollte wahrscheinlich in den meisten Fällen bevorzugt werden. Für meinen speziellen Anwendungsfall würde dies jedoch zu 6 Dateien führen, da ich dort verschiedene Dienste anbiete, die derzeit als Funktionen implementiert sind, und aus anderen Gründen bei 1 Datei bleiben möchte.
Alex
Leider funktioniert das bei mir nicht - ich rufe eine in meiner Shell-RC-Datei definierte Funktion auf, kann sie also nicht vernünftigerweise von einem anderen Ort aus ausführen
Xerus
3

Wenn Sie dies als Quelle angeben, wird das ignoriert #!und set -eauf die aufrufende Shell angewendet. Dies gilt auch für alle nachfolgenden Befehle.

Sie könnten eine Sub-Shell erzwingen:

copySomeStuff()
{
    (
        set -e
        source="$1"
        dest="$2"
        cp -r "$source" "$dest"
        return 0
    )
}

Auch zur Sicherheit:

  • Verwenden Sie nicht alle Caps-Variablennamen, da diese wahrscheinlich mit Umgebungsnamen kollidieren. (Wir hatten letzte Woche eine Frage, warum ein Skript nicht funktioniert hat, es hatte eine Variable PATH)

  • Verwenden Sie Anführungszeichen.

  • Immer Nutzung -toder -TOption für cp, mv, ln. z.B

    • cp -t destination_directory source_files …
    • cp -T source_file destination_file

    Beachten Sie, dass -tund -TGnu-Erweiterungen sind und unter einigen anderen Unixen nicht verfügbar sind. Aus Gründen der Portabilität können Sie anstelle der -tOption Folgendes tun:

    • cp source_files … destination_directory/

    Ich kenne keine alternative sichere Form für -T

Strg-Alt-Delor
quelle
2
Nur eine Anmerkung dazu cp -t, es ist eine GNU-Erweiterung, die auf BSD-Systemen nicht verfügbar ist. Wenn GNU coreutilsinstalliert ist, ist GNU cpmöglicherweise als verfügbar gcp.
Kusalananda
1
set -ein der Sub-Shell funktioniert super, danke! Vielen Dank auch für die Sicherheitshinweise.
Alex
Vielen Dank für diese Lösung, es ist perfekt. Es stellt auch sicher, dass set -enicht in der Berufungshell
Geedoubleya
Subshell funktioniert, außer dass stdout und stderr der Subshell verloren zu sein scheinen: /
Xerus
@Xerus Sie müssen eine neue Frage stellen, die ein Beispiel dafür zeigt, was Sie getan haben. Subsshell verliert weder stdout noch stderr.
Strg-Alt-Delor