So definieren Sie ein Shell-Skript, das nicht ausgeführt werden soll

40

Ich definiere ein Shell-Skript, das ein Benutzer sourceausführen soll, anstatt es auszuführen.

Gibt es eine herkömmliche oder intelligente Möglichkeit, dem Benutzer anzuzeigen, dass dies der Fall ist, beispielsweise über eine Dateierweiterung?

Gibt es Shell-Code, den ich in die Datei selbst schreiben kann, wodurch eine Meldung ausgegeben und die Datei beendet wird, wenn sie anstelle von sourced ausgeführt wird, damit der Benutzer diesen offensichtlichen Fehler vermeiden kann?

Algen
quelle
1
Wenn der Benutzer ein einzeiliges Shell-Skript schreibt x, das nur den Befehl enthält . your-script-to-be-sourced, ist dies in Ordnung. Wenn er es jedoch ausführen möchte, bash your-script-to-be-sourcedsollte dies verboten sein. Was ist der Sinn dieser Einschränkung?
user1934428
8
@ user1934428 Natürlich. Dies ist normal für ein Skript, das eine Reihe von envVariablen berechnet und diese als De-facto- Skriptausgabe belässt . Ein Anfänger bleibt tagelang mit dem Rätsel hängen, wenn Sie ihm erlauben, es auszuführen.
Kubanczyk

Antworten:

45

Angenommen, Sie führen bash aus, platzieren Sie den folgenden Code in der Nähe des Starts des Skripts, dessen Quelle Sie verwenden möchten, der jedoch nicht ausgeführt wird:

if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
    echo "Hey, you should source this script, not execute it!"
    exit 1
fi

Enthält unter Bash ${BASH_SOURCE[0]}den Namen der aktuellen Datei, die von der Shell gelesen wird, unabhängig davon, ob sie bezogen oder ausgeführt wird.

Im Gegensatz dazu $0ist der Name der aktuell ausgeführten Datei.

-eftestet, ob diese beiden Dateien dieselbe Datei sind. Wenn dies der Fall ist, benachrichtigen wir den Benutzer und beenden das Programm.

Weder POSIX -efnoch BASH_SOURCEPOSIX. Während -efvon ksh, yash, zsh und Dash unterstützt wird, ist BASH_SOURCEbash erforderlich. Inzsh jedoch ${BASH_SOURCE[0]}könnte ersetzt werden durch ${(%):-%N}.

John1024
quelle
2
Einfach echo "Usage: source \"$myfile\""
Kubanczyk
6
@kubanczyk sourceist nicht portabel. Wenn man bedenkt, dass diese Antwort bash-spezifisch ist, ist es nicht so schlimm, aber es ist eine gute Angewohnheit, das tragbare .
Gerät
33

Eine nicht ausführbare Datei kann aus dem Internet bezogen, aber nicht ausgeführt werden. Daher sollte es ein guter Hinweis sein, das Flag für die ausführbare Datei nicht zu setzen.

Edit: Trick, über den ich gerade gestolpert bin: mache den Shebang zu einer ausführbaren Datei, die kein Shell-Interpreter ist, und /bin/falselasse das Skript einen Fehler zurückgeben (rc! = 0)

#!/bin/false "This script should be sourced in a shell, not executed directly"
Magnet
quelle
7
Eine nicht ausführbare Datei kann jedoch weiterhin über zB bash somefile.sh...
twalberg
1
Wenn Sie wissen, dass Sie es mit bash(vd perl, python, awk ...) ausführen können , dann haben Sie in der Quelle
nachgesehen
1
Perl-Skripte haben im Allgemeinen den Namen somefile.plund Python somefile.py, also nein, ich habe die Kommentare wahrscheinlich nicht gelesen (was sind das überhaupt?) Und bash somefile.shist kürzer als chmod +x somefile.sh; ./somefile.sh...
twalberg
Auch einige Bourne-wie Muscheln, einschließlich bash, zunächst versuchen , execveeine Datei, aber wenn das nicht funktioniert, sie untersuchen die Datei manuell und die manuell zu interpretieren #!und rufen Sie es durch diesen Interpreter: das ist ein Erbe aus der Zeit , als das #!war ein rein User - Space Konvention, anstatt vom Kernel selbst gehandhabt zu werden. Ich denke bash , zumindest werde nicht tue dies für nicht ausführbare Dateien, aber ich weiß nicht , ob es tragbar ist so gesund Verhalten von allen Schalen zu erwarten , dass der Benutzer könnte das Skript aus wird aufgerufen wird .
mtraceur
"Wenn du weißt, dass du es mit bash ausführen kannst" Ähm, nein, manchmal hat der Benutzer einfach keine Ahnung, dass es andere Shells gibt als bash und das bash script.shkann gefährlich sein.
Sergiy Kolodyazhnyy
10

In diesem Beitrag zum Stapelüberlauf werden mehrere Methoden vorgeschlagen , von denen mir die von Wirawan Purwanto und mr.spuratic vorgeschlagene funktionsbasierte am besten gefallen hat :

Der robusteste Weg, wie von Wirawan Purwanto vorgeschlagen, ist die Überprüfung FUNCNAME[1] innerhalb einer Funktion :

function mycheck() { declare -p FUNCNAME; }
mycheck

Dann:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Dies ist das Äquivalent zur Überprüfung der Ausgabe caller, der Werte mainund der sourceUnterscheidung des Kontextes des Aufrufers. Mit FUNCNAME[]sparen Sie sich das Erfassen und Parsen von callerAusgaben. Sie müssen jedoch Ihre lokale Anruftiefe kennen oder berechnen, um korrekt zu sein. Fälle wie ein Skript, das aus einer anderen Funktion oder einem anderen Skript stammt, führen dazu, dass das Array (der Stapel) tiefer liegt. ( FUNCNAMEDies ist eine spezielle Bash-Array-Variable. Sie sollte zusammenhängende Indizes aufweisen, die dem Aufrufstapel entsprechen, sofern dies niemals der Fall ist unset.)

So können Sie am Anfang des Skripts Folgendes hinzufügen:

function check()
{
    if [[ ${FUNCNAME[-1]} != "source" ]]   # bash 4.2+, use ${FUNCNAME[@]: -1} for older
    then
        printf "Usage: source %s\n" "$0"
        exit 1
    fi
}
check
muru
quelle
7

Angenommen, es ist nicht schädlich, das Skript auszuführen, können Sie hinzufügen

return 0 || printf 'Must be sourced, not executed\n' >&2

bis zum Ende des Skripts. returnaußerhalb einer Funktion hat einen Exit-Code ungleich Null, es sei denn, die Datei wird bezogen.

chepner
quelle
3
Beachten Sie, dass dies den Exit-Status 0 zurückgibt. Versuchen Sie diese ähnliche Redewendung, die ich stattdessen verwende:return 2>/dev/null; echo "$0: This script must be sourced" 1>&2; exit 1
wjandrea
5

Wenn Sie ein Shell-Skript erstellen , wird die Shebang- Zeile ignoriert. Indem Sie einen ungültigen Shebang eingeben, können Sie den Benutzer darauf hinweisen, dass das Skript fälschlicherweise ausgeführt wurde:

#!/bin/bash source-this-script
# ...

Die Fehlermeldung lautet wie folgt:

/bin/bash: source-this-script: No such file or directory

Der (willkürliche) Argumentname gibt bereits einen starken Hinweis, aber die Fehlermeldung ist immer noch nicht 100% klar. Wir können dies mit einem Hilfsprogramm beheben source-this-script, das sich irgendwo in Ihrem befindet PATH:

#!/bin/sh
echo >&2 "This script must be sourced, not executed${1:+: }${1:-!}"
exit 1

Nun lautet die Fehlermeldung wie folgt:

This script must be sourced, not executed: path/to/script.sh

Vergleich zu anderen Ansätzen

Im Vergleich zu den anderen Antworten erfordert dieser Ansatz nur minimale Änderungen an jedem Skript (und eine Shebang-Zeile hilft bei der Erkennung von Dateitypen in Editoren und gibt den Shell-Skript-Dialekt an, sodass es sogar Vorteile gibt). Der Nachteil ist eine etwas unklare Fehlermeldung oder das (einmalige) Hinzufügen eines anderen Shell-Skripts.

Es verhindert jedoch nicht den expliziten Aufruf über bash path/to/script.sh(danke @muru!).

Ingo Karkat
quelle
1
Ein weiterer Nachteil ist, dass dies keinen Schutz bash some/script.shbietet, der auch den Schebang ignorieren würde.
muru
4
Sie können die Botschaft klarer machen, indem Sie den Schebang #!/bin/echo 'You must source this script!'oder so etwas machen.
Chris
1
@Chris: Richtig, aber dann würde ich die Dateityperkennung (zB in Vim) und die Dokumentation verlieren, um welchen Shell-Dialekt es sich handelt. Wenn Sie sich nicht für diese interessieren, würde Ihr Vorschlag tatsächlich das zweite Skript loswerden!
Ingo Karkat