Ich habe ein Skript, in dem ich nicht möchte, dass es aufgerufen wird, exit
wenn es bezogen wird.
Ich dachte daran zu prüfen, ob $0 == bash
dies jedoch Probleme bereitet, wenn das Skript von einem anderen Skript stammt oder wenn der Benutzer es von einer anderen Shell wie bezieht ksh
.
Gibt es eine zuverlässige Methode, um festzustellen, ob ein Skript bezogen wird?
Antworten:
Dies scheint zwischen Bash und Korn tragbar zu sein:
Eine ähnliche Zeile oder eine ähnliche Zuweisung
pathname="$_"
(mit einem späteren Test und einer späteren Aktion) muss sich in der ersten Zeile des Skripts oder in der Zeile nach dem Shebang befinden (die, falls verwendet, für ksh stehen sollte, damit sie funktioniert die meisten Umstände).quelle
BASH_ENV
, wird$_
oben im Skript der letzte Befehl ausgeführt, von dem aus ausgeführt wirdBASH_ENV
.$_
ein Problem.bash script
(Aufruf über Shell ausführbare Datei, die diese Lösung misreports als Quellen ) und (b) (weit weniger wahrscheinlich)echo bash; . script
(wenn$_
passiert , um die Schale passen das Skripts Sourcing dieser Lösung misreports es als eine Unterschale ). Nur Shell-spezifische Spezialvariablen (z. B.$BASH_SOURCE
) ermöglichen robuste Lösungen (daraus folgt, dass es keine robuste POSIX-kompatible Lösung gibt). Es ist möglich, wenn auch umständlich, einen robusten Cross-Shell-Test durchzuführen.Wenn Ihre Bash-Version die Array-Variable BASH_SOURCE kennt, versuchen Sie Folgendes:
quelle
${BASH_SOURCE[0]}
statt nur zu verwenden$BASH_SOURCE
? Und${0}
vs$0
?BASH_SOURCE
ist eine Array-Variable (siehe Handbuch ), die eine Stapelverfolgung von Quellen enthält, wobei${BASH_SOURCE[0]}
es sich um die neueste handelt. Die geschweiften Klammern werden hier verwendet, um der Bash mitzuteilen, was Teil des Variablennamens ist. Sie sind$0
in diesem Fall nicht notwendig , aber sie tun auch nicht weh. ;)$array
, erhalten Sie${array[0]}
standardmäßig. Gibt es also wieder einen Grund [...]?Robuste Lösungen für
bash
,ksh
,zsh
, eine darunter Querschale ein, sowie eine relativ robuste POSIX-kompatible Lösung :Die angegebenen Versionsnummern sind diejenigen, bei denen die Funktionalität überprüft wurde - wahrscheinlich funktionieren diese Lösungen auch bei viel früheren Versionen - Feedback ist willkommen .
Wenn Sie nur POSIX-Funktionen verwenden (z. B. in
dash
, das/bin/sh
unter Ubuntu funktioniert), gibt es keine zuverlässige Methode, um festzustellen, ob ein Skript bezogen wird. Die beste Annäherung finden Sie weiter unten .Einzeiler folgen - Erklärung unten; Die Cross-Shell-Version ist komplex, sollte aber robust funktionieren:
Bash (verifiziert am 3.57 und 4.4.19)
ksh (verifiziert auf 93u +)
zsh (überprüft unter 5.0.5) - Rufen Sie dies unbedingt außerhalb einer Funktion auf
Cross-Shell (Bash, Ksh, Zsh)
POSIX-konform ; kein Einzeiler (Einzel Pipeline) aus technischen Gründen und nicht vollständig robust (siehe unten):
Erläuterung:
Bash
Hinweis: Die Technik wurde aus der Antwort von user5754163 übernommen , da sie sich als robuster als die ursprüngliche Lösung herausstellte.
[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
[1]Bash erlaubt
return
Anweisungen nur von Funktionen und im obersten Bereich eines Skripts nur, wenn das Skript bezogen wird .return
es im obersten Bereich eines Skripts ohne Quellenangabe verwendet wird , wird eine Fehlermeldung ausgegeben und der Exit-Code auf gesetzt1
.(return 0 2>/dev/null)
wirdreturn
in einer Unterschale ausgeführt und unterdrückt die Fehlermeldung; Danach gibt der Exit-Code an, ob das Skript bezogen wurde (0
) oder nicht (1
). Dieser Code wird mit den Operatoren&&
und verwendet||
, um diesourced
Variable entsprechend festzulegen.return
im obersten Bereich eines Sourcing-Skripts das Skript würde.0
alsreturn
Operand robuster gemacht hat ; er bemerkt: per bash hilfe vonreturn [N]
: "Wenn N weggelassen wird, ist der Rückgabestatus der des letzten Befehls." Infolgedessen führt die frühere Version [die nurreturn
ohne Operanden verwendet wurde] zu einem falschen Ergebnis, wenn der letzte Befehl in der Shell des Benutzers einen Rückgabewert ungleich Null hat.ksh
Spezielle Variable
${.sh.file}
ist etwas analog zu$BASH_SOURCE
; Beachten Sie,${.sh.file}
dass dies einen Syntaxfehler in bash, zsh und dash verursacht. Führen Sie ihn daher unbedingt in Multi-Shell-Skripten unter bestimmten Bedingungen aus .Anders als in Bash,
$0
und${.sh.file}
es wird NICHT garantiert, dass sie im Fall ohne Quelle genau identisch sind, da es sich$0
möglicherweise um einen relativen Pfad handelt, obwohl${.sh.file}
es sich immer um einen vollständigen Pfad handelt,$0
muss dieser vor dem Vergleich in einen vollständigen Pfad aufgelöst werden.zsh
$ZSH_EVAL_CONTEXT
enthält Informationen zum Auswertungskontext - rufen Sie diese außerhalb einer Funktion auf. In einem Sourcing-Skript ['s oberster Bereich]$ZSH_EVAL_CONTEXT
endet mit:file
.Caveat: Innerhalb einer Kommandosubstitution, zsh Appends
:cmdsubst
, so Test$ZSH_EVAL_CONTEXT
für:file:cmdsubst$
dort.Nur POSIX-Funktionen verwenden
Wenn Sie bereit sind, bestimmte Annahmen zu treffen, können Sie eine vernünftige, aber nicht narrensichere Vermutung anstellen, ob Ihr Skript bezogen wird, basierend auf der Kenntnis der binären Dateinamen der Shells, die möglicherweise Ihr Skript ausführen .
Dies bedeutet insbesondere, dass dieser Ansatz fehlschlägt, wenn Ihr Skript von einem anderen Skript bezogen wird .
Der Abschnitt in „How to sourced Anrufungen Griff“ diese Antwort von mir bespricht die Grenzfälle , die nicht können mit POSIX behandelt werden nur im Detail kennzeichnet.
Dies beruht auf dem Standardverhalten von
$0
, daszsh
beispielsweise nicht vorliegt.Der sicherste Ansatz besteht daher darin , die oben genannten robusten, schalenspezifischen Methoden mit einer Fallback-Lösung für alle verbleibenden Schalen zu kombinieren .
Tipp des Hutes an Stéphane Desneux und seine Antwort für die Inspiration (Umwandlung meines
sh
muskelübergreifendenif
Anweisungsausdrucks in eine kompatible Anweisung und Hinzufügen eines Handlers für andere Shells).[1] user1902689 hat festgestellt, dass
[[ $0 != "$BASH_SOURCE" ]]
ein falsches Positiv ausgegeben wird , wenn Sie ein Skript in$PATH
ausführen, indem Sie seinen bloßen Dateinamen an diebash
Binärdatei übergeben. zBbash my-script
weil denn$0
dann gerade istmy-script
, wohingegen$BASH_SOURCE
der volle Weg ist . Während man normalerweise nicht diese Technik verwenden , um Skripte in die aufrufen$PATH
- sie würden gerade invoke sie direkt (my-script
) - es ist hilfreich , wenn es in Kombination-x
für das Debuggen .quelle
Nach dem Lesen der Antwort von @ DennisWilliamson gibt es einige Probleme, siehe unten:
Da steht diese Frage für ksh und Bashgibt es einen anderen Teil in dieser Antwort betreffend ksh... siehe unten.
Einfach Bash Weg
Lass es uns versuchen (on the fly, weil diese Bash es könnte ;-):
Ich benutze
source
stattdessen aus.
Gründen der Lesbarkeit (wie.
es ein Alias für istsource
):Beachten Sie, dass Prozessnummer nicht ändern , während Prozess Aufenthalt Quellen :
Warum nicht den
$_ == $0
Vergleich verwenden ?Um viele Fälle zu gewährleisten, beginne ich, ein echtes Skript zu schreiben :
Kopieren Sie dies in eine Datei mit dem Namen
testscript
:Jetzt konnten wir testen:
Das ist okay.
Das ist okay.
Zum Testen eines Skripts vor dem Hinzufügen eines
-x
Flags:Oder um vordefinierte Variablen zu verwenden:
Das wird nicht mehr funktionieren.
Wenn Sie den Kommentar von der 5. zur 6. Zeile verschieben, erhalten Sie eine besser lesbare Antwort:
Schwerer: ksh jetzt...
Da benutze ich nicht ksh viel, nachdem einige auf der Manpage gelesen haben, gibt es meine Versuche:
Kopieren Sie dies in
testfile.ksh
:Dann zwei Mal ausführen:
und sehen:
Es gibt eine Variable, die in einem Sourcing- Lauf vererbt wird, aber nichts, was wirklich damit zusammenhängt ...
Sie könnten sogar überprüfen, ob dies
$SECONDS
in der Nähe liegt0.000
, aber das stellt sicher, dass nur Fälle mit manueller Beschaffung ...Sie könnten sogar versuchen zu überprüfen, was Eltern sind:
Legen Sie dies in Ihre
testfile.ksh
:Als:
oder
ps ho cmd $PPID
, aber dies funktioniert nur für eine Ebene von Subsessions ...Entschuldigung, ich konnte unter keinen zuverlässigen Weg finden, dies zu tun ksh.
quelle
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]
für Skripte, die über pipe (cat script | bash
) eingelesen werden ..
kein Alias für istsource
, sondern umgekehrt.source somescript.sh
ist ein Bash-Ismus und nicht portabel,. somescript.sh
ist POSIX und portabel IIRC.Die
BASH_SOURCE[]
Antwort (bash-3.0 und höher) scheint am einfachsten zu sein, esBASH_SOURCE[]
ist jedoch nicht dokumentiert, dass sie außerhalb eines Funktionskörpers funktioniert (sie funktioniert derzeit in Übereinstimmung mit der Manpage).Der robusteste Weg, wie von Wirawan Purwanto vorgeschlagen, besteht darin,
FUNCNAME[1]
innerhalb einer Funktion zu überprüfen :Dann:
Dies entspricht der Überprüfung der Ausgabe
caller
der Wertemain
und dersource
Unterscheidung des Kontextes des Aufrufers. MitFUNCNAME[]
Speichern können Sie diecaller
Ausgabe erfassen und analysieren . 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. (FUNCNAME
Ist eine spezielle Bash-Array-Variable, sollte sie zusammenhängende Indizes haben, die dem Aufrufstapel entsprechen, solange dies niemals der Fall istunset
.)(In Bash-4.2 und höher können Sie
${FUNCNAME[-1]}
stattdessen das einfachere Formular für das letzte Element im Array verwenden. Verbessert und vereinfacht dank Dennis Williamsons Kommentar unten.)Ihr Problem lautet jedoch " Ich habe ein Skript, in dem ich nicht möchte, dass es" exit "aufruft, wenn es bezogen wird ". Die gängige
bash
Redewendung für diese Situation lautet:Wenn das Skript bezogen
return
wird, wird das bezogene Skript beendet und zum Anrufer zurückgekehrt.Wenn das Skript ausgeführt wird,
return
wird ein Fehler zurückgegeben (umgeleitet) undexit
das Skript wie gewohnt beendet. Beidesreturn
undexit
kann bei Bedarf einen Exit-Code annehmen.Leider funktioniert dies nicht in
ksh
(zumindest nicht in der von AT & T abgeleiteten Version, die ich hier habe), sondern wirdreturn
als gleichwertig behandelt ,exit
wenn es außerhalb einer Funktion oder eines Punktskripts aufgerufen wird.Aktualisiert : Was Sie können in der zeitgenössischen Versionen tun von
ksh
ist die spezielle Variable zu prüfen ,.sh.level
die mit der Funktion Aufruftiefe eingestellt ist. Bei einem aufgerufenen Skript wird dies zunächst deaktiviert, bei einem Punktskript wird es auf 1 gesetzt.Dies ist nicht ganz so robust wie die Bash-Version. Sie müssen
issourced()
in der Datei, die Sie testen, auf der obersten Ebene oder mit einer bekannten Funktionstiefe aufrufen .(Möglicherweise interessiert Sie auch dieser Code auf Github, der eine
ksh
Disziplin-Funktion und einige Debug-Trap-Tricks verwendet, um das Bash-FUNCNAME
Array zu emulieren .)Die kanonische Antwort hier: http://mywiki.wooledge.org/BashFAQ/109 bietet
$-
als weiteren Indikator (wenn auch nicht perfekt) den Shell-Status.Anmerkungen:
FUNCNAME[]
Solange jedoch nur das letzte Element in diesem Array getestet wird, besteht keine Mehrdeutigkeit.pdksh
. Das Nächste, was ich finden kann, gilt nur fürpdksh
, wo jedes Sourcing eines Skripts einen neuen Dateideskriptor öffnet (beginnend mit 10 für das ursprüngliche Skript). Mit ziemlicher Sicherheit nichts, worauf Sie sich verlassen möchten ...quelle
${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}
mit dem letzten (unteren) Gegenstand im Stapel? Dann war das Testen gegen "main" (für OP negieren) für mich am zuverlässigsten.PROMPT_COMMAND
Satz habe, wird dieser als letzter Index desFUNCNAME
Arrays angezeigt, wenn ich ihn ausführesource sourcetest.sh
. Das Umkehren des Schecks (dermain
als letzter Index gesucht wird ) scheint robuster zu sein :is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }
.FUNCNAME
nur in Funktionen verfügbar ist. Nach meinen Tests mitdeclare -p FUNCNAME
,bash
verhält sich anders. v4.3 gibt einen Fehler außerhalb von Funktionen aus, während v4.4 gibtdeclare -a FUNCNAME
. Beide (!) Rückkehrmain
für${FUNCNAME[0]}
im Haupt Skript (wenn es ausgeführt wird) , während$FUNCNAME
gibt nichts. Und: Es gibt so viele Skripte "ab", die$BASH_SOURCE
externe Funktionen verwenden, dass ich bezweifle, dass dies geändert werden kann oder wird.Anmerkung des Herausgebers: Die Lösung dieser Antwort funktioniert robust, ist jedoch
bash
nur. Es kann rationalisiert werden(return 2>/dev/null)
.TL; DR
Versuchen Sie, eine
return
Anweisung auszuführen . Wenn das Skript nicht bezogen wird, wird ein Fehler ausgelöst. Sie können diesen Fehler abfangen und nach Bedarf fortfahren.Fügen Sie dies in eine Datei ein und rufen Sie es beispielsweise test.sh auf:
Führen Sie es direkt aus:
Quelle:
Für mich funktioniert das in zsh und bash.
Erläuterung
Die
return
Anweisung löst einen Fehler aus, wenn Sie versuchen, sie außerhalb einer Funktion auszuführen, oder wenn das Skript nicht aus Quellen stammt. Versuchen Sie dies an einer Shell-Eingabeaufforderung:Sie müssen diese Fehlermeldung nicht sehen, damit Sie die Ausgabe nach dev / null umleiten können:
Überprüfen Sie nun den Exit-Code. 0 bedeutet OK (keine Fehler aufgetreten), 1 bedeutet, dass ein Fehler aufgetreten ist:
Sie möchten die
return
Anweisung auch innerhalb einer Sub-Shell ausführen . Wenn diereturn
Anweisung ausgeführt wird. . . Gut . . . kehrt zurück. Wenn Sie es in einer Unter-Shell ausführen, wird es aus dieser Unter-Shell zurückgegeben, anstatt aus Ihrem Skript zurückzukehren. Um es in der Sub-Shell auszuführen, wickeln Sie es ein in$(...)
:Jetzt können Sie den Exit-Code der Sub-Shell sehen, der 1 sein sollte, da in der Sub-Shell ein Fehler aufgetreten ist:
quelle
$ readlink $(which sh)
dash
$ . test.sh
This script is sourced.
$ ./test.sh
This script is sourced.
return
auf der obersten Ebene zu tun ist ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). Diedash
Shell behandelt areturn
auf der obersten Ebene alsexit
. Andere Muscheln mögenbash
oderzsh
erlauben nichtreturn
auf der obersten Ebene, was das Merkmal ist, das eine Technik wie diese ausnutzt.$
vor der Subshell entfernen . Das heißt, verwenden Sie(return >/dev/null 2>&1)
anstelle von$(return >/dev/null 2>&1)
- aber dann funktioniert es nicht mehr in Bash.dash
, wo diese Lösung nicht funktioniert, wirkt wiesh
auf Ubuntu zum Beispiel diese Lösung nicht generell die Arbeit mitsh
. Die Lösung funktioniert für mich in Bash 3.2.57 und 4.4.5 - mit oder ohne$
vorher(...)
(obwohl es nie einen guten Grund dafür gibt$
).return
Ohne einen expliziten Rückgabewert wird unterbrochen, wennsource
die Skripte direkt nach einem fehlerhaften Befehl ausgeführt werden. Vorgeschlagen die Verbesserung bearbeiten.FWIW, nachdem ich alle anderen Antworten gelesen hatte, fand ich folgende Lösung für mich:
Dies funktioniert für alle Skripte, die mit
#!/bin/bash
verschiedenen Shells beginnen, aber möglicherweise auch von diesen bezogen werden, um Informationen (wie Einstellungen) zu erhalten, die außerhalb dermain
Funktion gespeichert sind.Anstelle der letzten beiden Zeilen können Sie den folgenden (meiner Meinung nach weniger lesbaren) Code verwenden, um keine
BASH_SOURCE
anderen Shells festzulegen und dasset -e
Arbeiten zu ermöglichenmain
:Dieses Skriptrezept hat folgende Eigenschaften:
Wird auf
bash
normale Weise ausgeführt,main
wird aufgerufen. Bitte beachten Sie, dass dies keinen Aufruf wiebash -x script
(woscript
keinen Pfad enthält) beinhaltet, siehe unten.Wird von bezogen
bash
,main
wird nur aufgerufen, wenn das aufrufende Skript zufällig denselben Namen hat. (Zum Beispiel, wenn es sich selbst bezieht oder überbash -c 'someotherscript "$@"' main-script args..
womain-script
muss sein, wastest
sieht als$BASH_SOURCE
).Wenn
eval
von einer anderen Shell als bezogen, ausgeführt / gelesen / bearbeitet wirdbash
,main
wird dies nicht aufgerufen (unterscheidetBASH_SOURCE
sich immer von$0
).main
wird nicht aufgerufen, wennbash
das Skript von stdin gelesen wird, es sei denn, Sie legen$0
die leere Zeichenfolge wie folgt fest :( exec -a '' /bin/bash ) <script
Wenn dies
bash
miteval
(eval "`cat script`"
alle Anführungszeichen sind wichtig! ) In einem anderen Skript ausgewertet wird , wird dies aufgerufenmain
. Wenneval
es direkt über die Befehlszeile ausgeführt wird, ähnelt dies dem vorherigen Fall, in dem das Skript aus stdin gelesen wird. (BASH_SOURCE
ist leer, während$0
normalerweise,/bin/bash
wenn nicht zu etwas völlig anderem gezwungen wird.)Wenn
main
es nicht aufgerufen wird, gibt es returntrue
($?=0
) zurück.Dies beruht nicht auf unerwartetem Verhalten (zuvor habe ich undokumentiert geschrieben, aber ich habe keine Dokumentation gefunden, die Sie
unset
weder ändern noch ändernBASH_SOURCE
können):BASH_SOURCE
ist ein reserviertes Bash-Array . Das ZulassenBASH_SOURCE=".$0"
einer Änderung würde jedoch eine sehr gefährliche Dose Würmer öffnen. Ich gehe daher davon aus, dass dies keine Auswirkungen haben darf (außer vielleicht taucht in einer zukünftigen Version von eine hässliche Warnung aufbash
).BASH_SOURCE
die außerhalb von Funktionen funktioniert. Das Gegenteil (dass es nur in Funktionen funktioniert) ist jedoch nicht dokumentiert. Die Beobachtung ist, dass es funktioniert (getestet mitbash
v4.3 und v4.4, leider habe ich keinebash
v3.x mehr) und dass ziemlich viele Skripte kaputt gehen würden, wenn$BASH_SOURCE
sie nicht mehr wie beobachtet funktionieren. Daher ist meine Erwartung, dass dies auchBASH_SOURCE
für zukünftige Versionen von so bleibtbash
.( return 0 )
, was gibt,0
wenn bezogen und1
wenn nicht bezogen. Dies ist nicht nur für mich etwas unerwartet , und (laut den dortigen Messwerten) sagt POSIX, dassreturn
von Subshell undefiniertes Verhalten ist (und dasreturn
hier ist eindeutig von einer Subshell). Vielleicht wird diese Funktion irgendwann so weit verbreitet, dass sie nicht mehr geändert werden kann, aber AFAICS besteht eine viel höhere Wahrscheinlichkeit, dass eine zukünftigebash
Version versehentlich das Rückgabeverhalten in diesem Fall ändert.Läuft leider
bash -x script 1 2 3
nichtmain
. (Vergleichen Sie,script 1 2 3
woscript
kein Pfad hat). Folgendes kann als Problemumgehung verwendet werden:bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
bash script 1 2 3
läuft nichtmain
kann als Feature angesehen werden.Beachten Sie, dass
( exec -a none script )
Aufrufemain
(bash
nicht$0
an das Skript übergeben werden, dafür müssen Sie-c
wie im letzten Punkt gezeigt verwenden).Daher wird bis auf einige Eckfälle
main
nur aufgerufen, wenn das Skript wie gewohnt ausgeführt wird. Normalerweise ist dies das, was Sie wollen, insbesondere weil es keinen komplexen, schwer verständlichen Code gibt.Warum ich denke, dass dies ein guter allgemeiner Weg ist, um die Herausforderung zu lösen
Wenn Sie etwas haben, das von mehreren Shells bezogen werden kann, muss es kompatibel sein. Da es jedoch keine (einfach zu implementierende) tragbare Methode gibt, um das
source
Problem zu erkennen , sollten Sie die Regeln ändern (lesen Sie die anderen Antworten) .Indem Sie erzwingen, dass das Skript von ausgeführt werden muss
/bin/bash
, tun Sie genau dies.Dies löst alle Fälle, aber in diesem Fall kann das Skript nicht direkt ausgeführt werden:
/bin/bash
ist nicht installiert oder nicht funktionsfähig (d. h. in einer Boot-Umgebung)curl https://example.com/script | $SHELL
bash
Rezept aktuell genug ist. Es wird berichtet, dass dieses Rezept für bestimmte Varianten fehlschlägt. Überprüfen Sie daher, ob es für Ihren Fall funktioniert.)Ich kann mir jedoch keinen wirklichen Grund vorstellen, warum Sie das brauchen und auch die Möglichkeit, genau dasselbe Skript parallel zu beziehen! Normalerweise können Sie es einwickeln, um es
main
von Hand auszuführen . So wie das:$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
Anmerkungen
Diese Antwort wäre ohne die Hilfe aller anderen Antworten nicht möglich gewesen! Sogar die falschen - was mich anfangs dazu brachte, dies zu posten.
Update: Bearbeitet aufgrund der neuen Entdeckungen in https://stackoverflow.com/a/28776166/490291
quelle
/bin/sh
sich effektivbash
im POSIX-Modus befindet, wird das Zuweisen zuBASH_SOURCE
Unterbrechungen Ihres Skripts. In anderen Schalen (dash
,ksh
,zsh
), das Skript aufrufen , indem sie es als Datei Argument übergeben direkt an die Schale ausführbaren Störungen (zBzsh <your-script>
wird das Skript fälschlicherweise denken es ist sourced ). (Sie eigentlich schon erwähnt , dass Rohrleitungen , die Codefehlfunktionen in allen Schalen.). <your-script>
(Sourcing) im Prinzip von allen POSIX-ähnlichen Shells aus funktioniert, ist es nur sinnvoll, wenn das Skript explizit so geschrieben wurde, dass nur POSIX-Funktionen verwendet werden, um zu verhindern, dass Funktionen, die für eine Shell spezifisch sind, die Ausführung unterbrechen in anderen Schalen; Die Verwendung einer Bash- Shebang-Linie (anstelle von#!/bin/sh
) ist daher verwirrend - zumindest ohne einen auffälligen Kommentar. Umgekehrt ist es besser, die Ausführung in Nicht-Bash-Shells abzulehnen , wenn Ihr Skript nur über Bash ausgeführt werden soll (auch wenn nur nicht berücksichtigt wird, welche Funktionen möglicherweise nicht portierbar sind) .main
, aber dies geschieht in diesem Fall! Und wenn es von bezogen wird/bin/sh
,bash --posix
passiert dasselbe in diesem Fall, und das ist auch einfach falsch.Dies funktioniert später im Skript und hängt nicht von der Variablen _ ab:
oder
quelle
Ich werde eine BASH-spezifische Antwort geben. Korn Shell, sorry. Angenommen, Ihr Skriptname lautet
include2.sh
; dann mache eine Funktion innerhalb desinclude2.sh
aufgerufenenam_I_sourced
. Hier ist meine Demoversion voninclude2.sh
:Versuchen Sie nun, es auf viele Arten auszuführen:
Das funktioniert also ausnahmslos und es wird kein sprödes
$_
Zeug verwendet. Dieser Trick verwendet die Introspection-Funktion von BASH, dh integrierte VariablenFUNCNAME
undBASH_SOURCE
; Siehe ihre Dokumentation in der Bash-Handbuchseite.Nur zwei Einschränkungen:
1) der Anruf zu
am_I_called
müssen stattfinden , in dem sourced Skript, aber nicht in jeder Funktion, damit${FUNCNAME[1]}
kehrt etwas anderes. Ja ... du hättest es überprüfen können${FUNCNAME[2]}
- aber du machst dir das Leben nur schwerer.2) Die Funktion
am_I_called
muss sich im Sourcing-Skript befinden, wenn Sie herausfinden möchten, wie der Name der enthaltenen Datei lautet.quelle
Ich möchte eine kleine Korrektur für Dennis 'sehr hilfreiche Antwort vorschlagen , um sie etwas portabler zu machen. Ich hoffe:
weil
[[
von der (etwas anal zurückhaltenden IMHO) Debian POSIX-kompatiblen Shell nicht erkannt wird ,dash
. Möglicherweise benötigt man auch die Anführungszeichen, um sich vor Dateinamen zu schützen, die Leerzeichen enthalten, wiederum in der Shell.quelle
$_
ist ziemlich spröde. Sie müssen dies als erstes im Skript überprüfen. Und selbst dann ist nicht garantiert, dass es den Namen Ihrer Shell (falls bezogen) oder den Namen des Skripts (falls ausgeführt) enthält.Zum Beispiel, wenn der Benutzer festgelegt hat
BASH_ENV
,$_
enthält oben in einem Skript der Name des zuletzt im Befehl ausgeführten BefehlsBASH_ENV
Skript .Der beste Weg, den ich gefunden habe, ist zu verwenden
$0
:Leider funktioniert dieser Weg in zsh aufgrund der nicht sofort
functionargzero
Option mehr tut, als der Name vermuten lässt, und standardmäßig aktiviert ist.Um dies zu umgehen, habe ich
unsetopt functionargzero
meine.zshenv
.quelle
Ich folgte dem kompakten Ausdruck mklement0 .
Das ist ordentlich, aber ich habe festgestellt, dass es im Fall von ksh fehlschlagen kann, wenn es wie folgt aufgerufen wird:
(Es glaubt, dass es aus einer Quelle stammt und nicht, weil es eine Unterschale ausführt.) Aber der Ausdruck wird dies erkennen:
Auch wenn der Ausdruck kompakt ist, ist die Syntax nicht mit allen Shells kompatibel.
Also endete ich mit dem folgenden Code, der für bash, zsh, dash und ksh funktioniert
Fühlen Sie sich frei, exotische Muscheln Unterstützung hinzuzufügen :)
quelle
ksh 93+u
,ksh ./myscript.sh
funktioniert gut für mich (mit meiner Aussage) - welche Version verwenden Sie?/proc/$$/cmdline
) voraus und konzentriert sichdash
nur auf (was beispielsweise auchsh
unter Ubuntu funktioniert). Wenn Sie bereit sind, bestimmte Annahmen zu treffen, können Sie$0
einen vernünftigen, aber unvollständigen Test prüfen, der portabel ist.sh
/ istdash
.Ich glaube nicht, dass es eine tragbare Möglichkeit gibt, dies sowohl in ksh als auch in bash zu tun. In Bash konnte man es anhand der
caller
Ausgabe erkennen, aber ich glaube nicht, dass es in ksh ein Äquivalent gibt.quelle
$0
arbeitet inbash
,ksh93
undpdksh
. Ich muss nichtksh88
testen.Ich brauchte einen Einzeiler, der unter [mac, linux] mit bash.version> = 3 funktioniert, und keine dieser Antworten passt zur Rechnung.
quelle
bash
Lösung funktioniert gut (Sie könnten es vereinfachen$BASH_SOURCE
), aber dieksh
Lösung ist nicht robust: Wenn Ihr Skript von einem anderen Skript bezogen wird , erhalten Sie ein falsches Positiv.Auf den Punkt gebracht: Sie müssen auswerten, ob die Variable "$ 0" dem Namen Ihrer Shell entspricht.
So was:
Über SHELL :
Über QUELLE :
Es ist ziemlich schwer, eine zu haben 100% tragbare Weise zu erkennen, ob ein Skript bezogen wurde oder nicht.
In Bezug auf meine Erfahrung (7 Jahre mit Shellscripting) , der einzig sichere Weg (ohne sich auf Umgebungsvariablen mit PIDs usw. zu verlassen, was aufgrund der Tatsache, dass es sich um etwas VARIABLES handelt, nicht sicher ist ), sollten Sie:
Beide Optionen können nicht automatisch skaliert werden, dies ist jedoch der sicherere Weg.
Beispielsweise:
Wenn Sie ein Skript über eine SSH-Sitzung beziehen , lautet der von der Variablen "$ 0" (bei Verwendung von source ) zurückgegebene Wert -bash .
ODER
quelle
/bin/bash -c '. ./check_source.sh'
gibtThe script WAS NOT sourced.
. Gleicher Fehler:ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'
->The script WAS NOT sourced.
Am Ende habe ich nachgesehen
[[ $_ == "$(type -p "$0")" ]]
Bei Verwendung
curl ... | bash -s -- ARGS
zum Ausführen von Remote-Skripten im laufenden Betrieb sind die $ 0 beim Ausführen der tatsächlichen Skriptdatei nichtbash
normal/bin/bash
, dahertype -p "$0"
zeige ich den vollständigen Pfad der Bash an.Prüfung:
quelle
Dies ist ein Nebeneffekt einiger anderer Antworten in Bezug auf die "universelle" Cross-Shell-Unterstützung. Dies ist zugegebenermaßen insbesondere https://stackoverflow.com/a/2942183/3220983 sehr ähnlich , wenn auch etwas anders. Die Schwäche dabei ist, dass ein Client-Skript die Verwendung respektieren muss (dh indem zuerst eine Variable exportiert wird). Die Stärke ist, dass dies einfach ist und "überall" funktionieren sollte. Hier ist eine Vorlage für Ihr Vergnügen beim Ausschneiden und Einfügen:
Hinweis: Ich stelle
export
nur sicher, dass dieser Mechanismus auf Unterprozesse erweitert werden kann.quelle