So erkennen Sie, ob ein Skript bezogen wird

217

Ich habe ein Skript, in dem ich nicht möchte, dass es aufgerufen wird, exitwenn es bezogen wird.

Ich dachte daran zu prüfen, ob $0 == bashdies 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?

Brianegge
quelle
2
Ich hatte vor einiger Zeit ein ähnliches Problem und löste es, indem ich in allen Fällen das „Verlassen“ vermeidete. "kill -INT $$" beendet das Skript in beiden Fällen sicher.
JESii
1
Haben Sie diese Antwort bemerkt ? Es wird 5 Jahre später von der akzeptierten gegeben, aber es hat "Batterien enthalten".
Raratiru

Antworten:

72

Dies scheint zwischen Bash und Korn tragbar zu sein:

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

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).

Bis auf weiteres angehalten.
quelle
10
Leider funktioniert es nicht garantiert. Wenn der Benutzer festgelegt hat BASH_ENV, wird $_oben im Skript der letzte Befehl ausgeführt, von dem aus ausgeführt wird BASH_ENV.
Mikel
30
Dies funktioniert auch nicht, wenn Sie bash zum Ausführen des Skripts verwenden, z. B. $ bash script.sh. Dann wäre $ _ / bin / bash anstelle von ./script.sh. Dies ist der Fall, den Sie beim Aufrufen des Skripts erwarten auf diese Weise: $ ./script.sh In jedem Fall ist das Erkennen mit $_ein Problem.
Wirawan Purwanto
2
Zusätzliche Tests könnten enthalten sein, um nach diesen Aufrufmethoden zu suchen.
Bis auf weiteres angehalten.
8
Leider ist das falsch! siehe meine Antwort
F. Hauri
8
Zusammenfassend: Während dieser Ansatz normalerweise funktioniert, ist er nicht robust . es scheitert in den folgenden 2 - Szenarien: (A) 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.
mklement0
170

Wenn Ihre Bash-Version die Array-Variable BASH_SOURCE kennt, versuchen Sie Folgendes:

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
Barroyo
quelle
11
Das ist vielleicht der sauberste Weg, da $ BASH_SOURCE genau für diesen Zweck vorgesehen ist.
con-f-use
4
Beachten Sie, dass dies unter ksh nicht funktioniert. Dies ist eine Bedingung, die vom OP angegeben wurde.
Bis auf weiteres angehalten.
2
Gibt es einen Grund, ${BASH_SOURCE[0]}statt nur zu verwenden $BASH_SOURCE? Und ${0}vs $0?
Hraban
4
BASH_SOURCEist 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 $0in diesem Fall nicht notwendig , aber sie tun auch nicht weh. ;)
Konrad
4
@Konrad, und wenn Sie erweitern $array, erhalten Sie ${array[0]}standardmäßig. Gibt es also wieder einen Grund [...]?
Charles Duffy
133

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/shunter 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)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (verifiziert auf 93u +)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
  • zsh (überprüft unter 5.0.5) - Rufen Sie dies unbedingt außerhalb einer Funktion auf

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • Cross-Shell (Bash, Ksh, Zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
  • POSIX-konform ; kein Einzeiler (Einzel Pipeline) aus technischen Gründen und nicht vollständig robust (siehe unten):

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1 
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi

Erläuterung:


Bash

(return 0 2>/dev/null) && sourced=1 || sourced=0

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 returnAnweisungen nur von Funktionen und im obersten Bereich eines Skripts nur, wenn das Skript bezogen wird .

    • Wenn returnes im obersten Bereich eines Skripts ohne Quellenangabe verwendet wird , wird eine Fehlermeldung ausgegeben und der Exit-Code auf gesetzt 1.
  • (return 0 2>/dev/null)wird returnin 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 die sourcedVariable entsprechend festzulegen.

    • Die Verwendung einer Subshell ist erforderlich, da ausgeführt wird return im obersten Bereich eines Sourcing-Skripts das Skript würde.
    • Tipp des Hutes an @Haozhun , der den Befehl durch explizite Verwendung 0als returnOperand robuster gemacht hat ; er bemerkt: per bash hilfe von return [N]: "Wenn N weggelassen wird, ist der Rückgabestatus der des letzten Befehls." Infolgedessen führt die frühere Version [die nur returnohne Operanden verwendet wurde] zu einem falschen Ergebnis, wenn der letzte Befehl in der Shell des Benutzers einen Rückgabewert ungleich Null hat.

ksh

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0

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, $0und ${.sh.file}es wird NICHT garantiert, dass sie im Fall ohne Quelle genau identisch sind, da es sich $0möglicherweise um einen relativen Pfad handelt, obwohl ${.sh.file}es sich immer um einen vollständigen Pfad handelt, $0muss dieser vor dem Vergleich in einen vollständigen Pfad aufgelöst werden.


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXTenthä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_CONTEXTfü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, das zshbeispielsweise 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 shmuskelübergreifenden ifAnweisungsausdrucks in eine kompatible Anweisung und Hinzufügen eines Handlers für andere Shells).

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[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 die bashBinärdatei übergeben. zB bash my-scriptweil denn $0dann gerade ist my-script, wohingegen $BASH_SOURCEder 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 -xfür das Debuggen .

mklement0
quelle
1
Ein großes Lob für eine so umfassende Antwort.
DrUseful
75

Nach dem Lesen der Antwort von @ DennisWilliamson gibt es einige Probleme, siehe unten:

Da steht diese Frage für und gibt es einen anderen Teil in dieser Antwort betreffend ... siehe unten.

Einfach Weg

[ "$0" = "$BASH_SOURCE" ]

Lass es uns versuchen (on the fly, weil diese Bash es könnte ;-):

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

Ich benutze sourcestattdessen aus .Gründen der Lesbarkeit (wie .es ein Alias ​​für ist source):

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

Beachten Sie, dass Prozessnummer nicht ändern , während Prozess Aufenthalt Quellen :

echo $$
29301

Warum nicht den $_ == $0Vergleich verwenden ?

Um viele Fälle zu gewährleisten, beginne ich, ein echtes Skript zu schreiben :

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

Kopieren Sie dies in eine Datei mit dem Namen testscript:

cat >testscript   
chmod +x testscript

Jetzt konnten wir testen:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)

Das ist okay.

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

Das ist okay.

Zum Testen eines Skripts vor dem Hinzufügen eines -xFlags:

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)

Oder um vordefinierte Variablen zu verwenden:

env PATH=/tmp/bintemp:$PATH ./testscript 
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript 
proc: 25972[ppid:24890] is own (DW purpose: sourced)

Das wird nicht mehr funktionieren.

Wenn Sie den Kommentar von der 5. zur 6. Zeile verschieben, erhalten Sie eine besser lesbare Antwort:

./testscript 
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript 
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript 
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

Schwerer: jetzt...

Da benutze ich nicht viel, nachdem einige auf der Manpage gelesen haben, gibt es meine Versuche:

#!/bin/ksh

set >/tmp/ksh-$$.log

Kopieren Sie dies in testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

Dann zwei Mal ausführen:

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

und sehen:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

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 $SECONDSin der Nähe liegt 0.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:

ps $PPID

Als:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

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 .

F. Hauri
quelle
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]für Skripte, die über pipe ( cat script | bash) eingelesen werden .
hakre
2
Beachten Sie, dass dies .kein Alias ​​für ist source, sondern umgekehrt. source somescript.shist ein Bash-Ismus und nicht portabel, . somescript.shist POSIX und portabel IIRC.
Dragon788
32

Die BASH_SOURCE[]Antwort (bash-3.0 und höher) scheint am einfachsten zu sein, es BASH_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 :

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 entspricht der Überprüfung der Ausgabe callerder Werte mainund der sourceUnterscheidung des Kontextes des Aufrufers. Mit FUNCNAME[]Speichern können Sie die callerAusgabe 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. ( FUNCNAMEIst eine spezielle Bash-Array-Variable, sollte sie zusammenhängende Indizes haben, die dem Aufrufstapel entsprechen, solange dies niemals der Fall ist unset.)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(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 bashRedewendung für diese Situation lautet:

return 2>/dev/null || exit

Wenn das Skript bezogen returnwird, wird das bezogene Skript beendet und zum Anrufer zurückgekehrt.

Wenn das Skript ausgeführt wird, returnwird ein Fehler zurückgegeben (umgeleitet) und exitdas Skript wie gewohnt beendet. Beides returnund exitkann 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 wird returnals gleichwertig behandelt , exitwenn es außerhalb einer Funktion oder eines Punktskripts aufgerufen wird.

Aktualisiert : Was Sie können in der zeitgenössischen Versionen tun von kshist die spezielle Variable zu prüfen , .sh.leveldie mit der Funktion Aufruftiefe eingestellt ist. Bei einem aufgerufenen Skript wird dies zunächst deaktiviert, bei einem Punktskript wird es auf 1 gesetzt.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

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 kshDisziplin-Funktion und einige Debug-Trap-Tricks verwendet, um das Bash- FUNCNAMEArray zu emulieren .)

Die kanonische Antwort hier: http://mywiki.wooledge.org/BashFAQ/109 bietet $-als weiteren Indikator (wenn auch nicht perfekt) den Shell-Status.


Anmerkungen:

  • Es ist möglich, Bash-Funktionen mit den Namen "main" und "source" zu erstellen (wobei das eingebaute Element überschrieben wird ). Diese Namen werden möglicherweise in angezeigt. FUNCNAME[]Solange jedoch nur das letzte Element in diesem Array getestet wird, besteht keine Mehrdeutigkeit.
  • Ich habe keine gute Antwort auf pdksh. Das Nächste, was ich finden kann, gilt nur für pdksh, 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 ...
mr.spuratic
quelle
Wie wäre es ${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.
Adrian Günter
Wenn ich einen PROMPT_COMMANDSatz habe, wird dieser als letzter Index des FUNCNAMEArrays angezeigt, wenn ich ihn ausführe source sourcetest.sh. Das Umkehren des Schecks (der mainals letzter Index gesucht wird ) scheint robuster zu sein : is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }.
dimo414
1
Die Manpage gibt an, dass dies FUNCNAMEnur in Funktionen verfügbar ist. Nach meinen Tests mit declare -p FUNCNAME, bashverhält sich anders. v4.3 gibt einen Fehler außerhalb von Funktionen aus, während v4.4 gibt declare -a FUNCNAME. Beide (!) Rückkehr mainfür ${FUNCNAME[0]}im Haupt Skript (wenn es ausgeführt wird) , während $FUNCNAMEgibt nichts. Und: Es gibt so viele Skripte "ab", die $BASH_SOURCEexterne Funktionen verwenden, dass ich bezweifle, dass dies geändert werden kann oder wird.
Tino
24

Anmerkung des Herausgebers: Die Lösung dieser Antwort funktioniert robust, ist jedoch bashnur. Es kann rationalisiert werden
(return 2>/dev/null).

TL; DR

Versuchen Sie, eine returnAnweisung 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:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

Führen Sie es direkt aus:

shell-prompt> sh test.sh
output: This script is not sourced.

Quelle:

shell-prompt> source test.sh
output: This script is sourced.

Für mich funktioniert das in zsh und bash.

Erläuterung

Die returnAnweisung 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:

shell-prompt> return
output: ...can only `return` from a function or sourced script

Sie müssen diese Fehlermeldung nicht sehen, damit Sie die Ausgabe nach dev / null umleiten können:

shell-prompt> return >/dev/null 2>&1

Überprüfen Sie nun den Exit-Code. 0 bedeutet OK (keine Fehler aufgetreten), 1 bedeutet, dass ein Fehler aufgetreten ist:

shell-prompt> echo $?
output: 1

Sie möchten die returnAnweisung auch innerhalb einer Sub-Shell ausführen . Wenn die returnAnweisung 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 $(...):

shell-prompt> $(return >/dev/null 2>$1)

Jetzt können Sie den Exit-Code der Sub-Shell sehen, der 1 sein sollte, da in der Sub-Shell ein Fehler aufgetreten ist:

shell-prompt> echo $?
output: 1
user5754163
quelle
Dies schlägt für mich in 0.5.8-2.1ubuntu2$ readlink $(which sh) dash $ . test.sh This script is sourced. $ ./test.sh This script is sourced.
Phil Rutschman
3
POSIX gibt nicht an, was returnauf der obersten Ebene zu tun ist ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). Die dashShell behandelt a returnauf der obersten Ebene als exit. Andere Muscheln mögen bashoder zsherlauben nicht returnauf der obersten Ebene, was das Merkmal ist, das eine Technik wie diese ausnutzt.
user5754163
Es funktioniert in sh, wenn Sie die $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.
Gleichnamiger
@Eponymous: Da dash, wo diese Lösung nicht funktioniert, wirkt wie shauf Ubuntu zum Beispiel diese Lösung nicht generell die Arbeit mit sh. 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 $).
mklement0
2
returnOhne einen expliziten Rückgabewert wird unterbrochen, wenn sourcedie Skripte direkt nach einem fehlerhaften Befehl ausgeführt werden. Vorgeschlagen die Verbesserung bearbeiten.
DimG
12

FWIW, nachdem ich alle anderen Antworten gelesen hatte, fand ich folgende Lösung für mich:

Update: Tatsächlich hat jemand in einer anderen Antwort einen seither korrigierten Fehler entdeckt , der auch meine betraf. Ich denke, das Update hier ist auch eine Verbesserung (siehe Änderungen, wenn Sie neugierig sind).

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 der mainFunktion gespeichert sind.

Nach den Kommentaren unten funktioniert diese Antwort hier anscheinend nicht für alle bashVarianten. Auch nicht für Systeme, auf denen /bin/shbasiert bash. IE schlägt es für bashv3.x unter MacOS fehl . (Derzeit weiß ich nicht, wie ich das lösen soll.)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

Anstelle der letzten beiden Zeilen können Sie den folgenden (meiner Meinung nach weniger lesbaren) Code verwenden, um keine BASH_SOURCEanderen Shells festzulegen und das set -eArbeiten zu ermöglichen main:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

Dieses Skriptrezept hat folgende Eigenschaften:

  • Wird auf bashnormale Weise ausgeführt, mainwird aufgerufen. Bitte beachten Sie, dass dies keinen Aufruf wie bash -x script(wo scriptkeinen Pfad enthält) beinhaltet, siehe unten.

  • Wird von bezogen bash, mainwird nur aufgerufen, wenn das aufrufende Skript zufällig denselben Namen hat. (Zum Beispiel, wenn es sich selbst bezieht oder über bash -c 'someotherscript "$@"' main-script args..wo main-scriptmuss sein, was testsieht als $BASH_SOURCE).

  • Wenn evalvon einer anderen Shell als bezogen, ausgeführt / gelesen / bearbeitet wird bash, mainwird dies nicht aufgerufen (unterscheidet BASH_SOURCEsich immer von $0).

  • mainwird nicht aufgerufen, wenn bashdas Skript von stdin gelesen wird, es sei denn, Sie legen $0die leere Zeichenfolge wie folgt fest :( exec -a '' /bin/bash ) <script

  • Wenn dies bashmit eval ( eval "`cat script`" alle Anführungszeichen sind wichtig! ) In einem anderen Skript ausgewertet wird , wird dies aufgerufen main. Wenn evales direkt über die Befehlszeile ausgeführt wird, ähnelt dies dem vorherigen Fall, in dem das Skript aus stdin gelesen wird. ( BASH_SOURCEist leer, während $0normalerweise, /bin/bashwenn nicht zu etwas völlig anderem gezwungen wird.)

  • Wenn maines nicht aufgerufen wird, gibt es return true( $?=0) zurück.

  • Dies beruht nicht auf unerwartetem Verhalten (zuvor habe ich undokumentiert geschrieben, aber ich habe keine Dokumentation gefunden, die Sie unsetweder ändern noch ändern BASH_SOURCEkönnen):

    • BASH_SOURCEist ein reserviertes Bash-Array . Das Zulassen BASH_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 auf bash).
    • Es gibt keine Dokumentation, BASH_SOURCEdie außerhalb von Funktionen funktioniert. Das Gegenteil (dass es nur in Funktionen funktioniert) ist jedoch nicht dokumentiert. Die Beobachtung ist, dass es funktioniert (getestet mit bashv4.3 und v4.4, leider habe ich keine bashv3.x mehr) und dass ziemlich viele Skripte kaputt gehen würden, wenn $BASH_SOURCEsie nicht mehr wie beobachtet funktionieren. Daher ist meine Erwartung, dass dies auch BASH_SOURCEfür zukünftige Versionen von so bleibt bash.
    • Im Gegensatz dazu (schöner Fund, übrigens!) Überlegen Sie ( return 0 ), was gibt, 0wenn bezogen und 1wenn nicht bezogen. Dies ist nicht nur für mich etwas unerwartet , und (laut den dortigen Messwerten) sagt POSIX, dass returnvon Subshell undefiniertes Verhalten ist (und das returnhier 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ünftige bashVersion versehentlich das Rückgabeverhalten in diesem Fall ändert.
  • Läuft leider bash -x script 1 2 3nicht main. (Vergleichen Sie, script 1 2 3wo scriptkein Pfad hat). Folgendes kann als Problemumgehung verwendet werden:

    • bash -x "`which script`" 1 2 3
    • bash -xc '. script' "`which script`" 1 2 3
    • Das bash script 1 2 3läuft nicht mainkann als Feature angesehen werden.
  • Beachten Sie, dass ( exec -a none script )Aufrufe main( bashnicht $0an das Skript übergeben werden, dafür müssen Sie -cwie im letzten Punkt gezeigt verwenden).

Daher wird bis auf einige Eckfälle mainnur 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.

Beachten Sie, dass es dem Python-Code sehr ähnlich ist:

if __name__ == '__main__': main()

Dies verhindert auch das Aufrufen von main, mit Ausnahme einiger Eckfälle, da Sie das Skript importieren / laden und dies erzwingen können__name__='__main__'

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 sourceProblem 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)
  • Wenn Sie es zu einer Shell wie in leiten curl https://example.com/script | $SHELL
  • (Hinweis: Dies gilt nur, wenn Ihr bashRezept 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 mainvon 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

Tino
quelle
Getestet auf ksh und bash-4.3. Nett. Es ist so schade, dass Ihre Antwort ein hartes Leben haben wird, da die anderen Antworten bereits Jahre damit verbracht haben, Stimmen zu sammeln.
Hagello
Danke für diese Antwort. Ich habe den längeren, weniger lesbaren Test mit der IF-Anweisung sehr geschätzt, da es hilfreich ist, beide Situationen zu behandeln, um zumindest einen nicht stillen Fehler zu verursachen. In meinem Fall muss ein Skript bezogen werden, oder der Benutzer muss auf andere Weise über seinen Fehler informiert werden, wenn er die Quelle nicht verwendet.
Tim Richardson
@Tino: Wie für "könnte auch von verschiedenen Shells bezogen werden": Unter macOS, wo /bin/shsich effektiv bashim POSIX-Modus befindet, wird das Zuweisen zu BASH_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 (zB zsh <your-script>wird das Skript fälschlicherweise denken es ist sourced ). (Sie eigentlich schon erwähnt , dass Rohrleitungen , die Codefehlfunktionen in allen Schalen.)
mklement0
@Tino: Nebenbei: Während . <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) .
mklement0
1
@ mklement0 Nochmals vielen Dank, fügte hinzu, dass ein Problem vorliegt. Für andere Leser: Wenn es mit bash v3.x bezogen wird, sollte es nicht ausgeführt werden main, aber dies geschieht in diesem Fall! Und wenn es von bezogen wird /bin/sh, bash --posixpassiert dasselbe in diesem Fall, und das ist auch einfach falsch.
Tino
6

Dies funktioniert später im Skript und hängt nicht von der Variablen _ ab:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

oder

[ $(basename $0) = $Prog ] && exit
Jim Mcnamara
quelle
1
Ich denke, diese Antwort ist eine der wenigen POSIX-kompatiblen hier. Mit den offensichtlichen Nachteilen, dass Sie den Dateinamen kennen müssen und es nicht funktioniert, wenn beide Skripte den gleichen Dateinamen haben.
JepZ
5

Ich werde eine BASH-spezifische Antwort geben. Korn Shell, sorry. Angenommen, Ihr Skriptname lautet include2.sh; dann mache eine Funktion innerhalb des include2.shaufgerufenen am_I_sourced. Hier ist meine Demoversion von include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

Versuchen Sie nun, es auf viele Arten auszuführen:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

Das funktioniert also ausnahmslos und es wird kein sprödes $_Zeug verwendet. Dieser Trick verwendet die Introspection-Funktion von BASH, dh integrierte Variablen FUNCNAMEund BASH_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.

Wirawan Purwanto
quelle
1
Erläuterung: Für diese Funktion ist BASH Version 3+ erforderlich. In BASH 2 ist FUNCNAME eine skalare Variable anstelle eines Arrays. Auch BASH 2 hat keine Array-Variable BASH_SOURCE.
Wirawan Purwanto
4

Ich möchte eine kleine Korrektur für Dennis 'sehr hilfreiche Antwort vorschlagen , um sie etwas portabler zu machen. Ich hoffe:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

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.

user354193
quelle
2

$_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 :

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

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 functionargzeromeine .zshenv.

Mikel
quelle
1

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:

/bin/ksh -c ./myscript.sh

(Es glaubt, dass es aus einer Quelle stammt und nicht, weil es eine Unterschale ausführt.) Aber der Ausdruck wird dies erkennen:

/bin/ksh ./myscript.sh

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

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

Fühlen Sie sich frei, exotische Muscheln Unterstützung hinzuzufügen :)

Stéphane Desneux
quelle
In ksh 93+u, ksh ./myscript.shfunktioniert gut für mich (mit meiner Aussage) - welche Version verwenden Sie?
mklement0
Ich fürchte, es gibt keine Möglichkeit, zuverlässig festzustellen, ob ein Skript nur mit POSIX-Funktionen bezogen wird: Ihr Versuch setzt Linux ( /proc/$$/cmdline) voraus und konzentriert sich dashnur auf (was beispielsweise auch shunter Ubuntu funktioniert). Wenn Sie bereit sind, bestimmte Annahmen zu treffen, können Sie $0einen vernünftigen, aber unvollständigen Test prüfen, der portabel ist.
mklement0
++ für den grundlegenden Ansatz - Ich habe mir die Freiheit genommen, ihn in einem Nachtrag zu meiner Antwort an das anzupassen, was meiner Meinung nach die beste tragbare Annäherung an die Unterstützung von sh/ ist dash.
mklement0
0

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 callerAusgabe erkennen, aber ich glaube nicht, dass es in ksh ein Äquivalent gibt.

Michal Čihař
quelle
$0arbeitet in bash, ksh93und pdksh. Ich muss nicht ksh88testen.
Mikel
0

Ich brauchte einen Einzeiler, der unter [mac, linux] mit bash.version> = 3 funktioniert, und keine dieser Antworten passt zur Rechnung.

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
Karsten
quelle
1
Die bashLösung funktioniert gut (Sie könnten es vereinfachen $BASH_SOURCE), aber die kshLösung ist nicht robust: Wenn Ihr Skript von einem anderen Skript bezogen wird , erhalten Sie ein falsches Positiv.
mklement0
0

Auf den Punkt gebracht: Sie müssen auswerten, ob die Variable "$ 0" dem Namen Ihrer Shell entspricht.


So was:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


Über SHELL :

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

Über QUELLE :

$ source check_source.sh
First Parameter: bash

The script was sourced.



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:

  • Erweitern Sie die Möglichkeiten von Ihrem if
  • mit switch / case, wenn du willst.

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 .

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

ODER

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi
ivanleoncz
quelle
2
Abgestimmt, da dies eindeutig falsch ist: /bin/bash -c '. ./check_source.sh'gibt The script WAS NOT sourced.. Gleicher Fehler: ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'->The script WAS NOT sourced.
Tino
2
Ihre Ablehnung hat das gesamte Szenario verändert und einen großen Beitrag geleistet, Tino. Vielen Dank!
ivanleoncz
0

Am Ende habe ich nachgesehen [[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

Bei Verwendung curl ... | bash -s -- ARGSzum Ausführen von Remote-Skripten im laufenden Betrieb sind die $ 0 beim Ausführen der tatsächlichen Skriptdatei nicht bashnormal /bin/bash, daher type -p "$0"zeige ich den vollständigen Pfad der Bash an.

Prüfung:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE
osexp2003
quelle
0

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:

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

Hinweis: Ich stelle exportnur sicher, dass dieser Mechanismus auf Unterprozesse erweitert werden kann.

BuvinJ
quelle