6176 Stimmen

Wie kann ich das Quellverzeichnis eines Bash-Skripts aus dem Skript selbst abrufen?

Wie erhalte ich den Pfad des Verzeichnisses, in dem ein Bash Skript befindet sich, innerhalb dieses Drehbuch?

Ich möchte ein Bash-Skript als Launcher für eine andere Anwendung verwenden. Ich möchte das Arbeitsverzeichnis in das Verzeichnis ändern, in dem sich das Bash-Skript befindet, damit ich mit den Dateien in diesem Verzeichnis arbeiten kann:

$ ./application

91 Stimmen

Keine der derzeitigen Lösungen funktioniert, wenn es irgendwelche Zeilenumbrüche am Ende des Verzeichnisnamens - Sie werden von der Befehlssubstitution entfernt. Um dies zu umgehen, können Sie ein Nicht-Neuzeilen-Zeichen innerhalb der Befehlsersetzung anhängen - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - und entfernen Sie es ohne Befehlssubstitution - DIR="${DIR%x}" .

94 Stimmen

@jpmc26 Es gibt zwei sehr häufige Situationen: Unfälle und Sabotage. Ein Skript sollte nicht auf unvorhersehbare Weise versagen, nur weil jemand, irgendwo, eine mkdir $'\n' .

36 Stimmen

Wer Leute sein System auf diese Weise sabotieren lässt, sollte es nicht der Bash überlassen, solche Probleme zu erkennen... und schon gar nicht Leute einstellen, die zu solchen Fehlern fähig sind. Ich habe in den 25 Jahren, in denen ich die Bash benutze, noch nie erlebt, dass so etwas irgendwo passiert.... Deshalb gibt es Dinge wie Perl und Praktiken wie Taint Checking (ich werde wahrscheinlich dafür geflamed werden, dass ich das sage :)

13voto

User8461 Punkte 336

Dies sind kurze Wege, um Informationen zum Skript zu erhalten:

Ordner und Dateien:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Verwenden Sie diese Befehle:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

Und ich erhielt diese Ausgabe:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

Siehe auch: https://pastebin.com/J8KjxrPF

0 Stimmen

Ich denke, meine Antwort ist in Ordnung, denn es ist schwer, eine einfache funktionierende Ausgabe zu finden. Hier können Sie den Code nehmen, den Sie mögen, z.B. cd + pwd, dirname + realpath oder dirname + readlink. Ich bin mir nicht sicher, ob alle Teile schon existieren und die meisten Antworten sind komplex und überladen. Hier können Sie den Code herauspicken, den Sie verwenden möchten. Zumindest bitte nicht entfernen, da ich es in Zukunft brauche :D

13voto

Matt Tardiff Punkte 8721

Dies funktioniert in Bash 3.2:

path="$( dirname "$( which "$0" )" )"

Wenn Sie eine ~/bin Verzeichnis in Ihrem $PATH haben Sie A innerhalb dieses Verzeichnisses. Es verweist auf das Skript ~/bin/lib/B . Sie wissen, wo sich das eingefügte Skript im Verhältnis zum ursprünglichen Skript befindet, nämlich in der lib Unterverzeichnis, aber nicht dort, wo es relativ zum aktuellen Verzeichnis des Benutzers liegt.

Dies wird wie folgt gelöst (innerhalb A ):

source "$( dirname "$( which "$0" )" )/lib/B"

Es spielt keine Rolle, wo sich der Benutzer befindet oder wie er das Skript aufruft. Es wird immer funktionieren.

5 Stimmen

Der Punkt auf which ist sehr umstritten. type , hash und andere Builtins machen das Gleiche in der Bash besser. which ist etwas tragbarer, obwohl es nicht dasselbe ist which die in anderen Shells wie tcsh verwendet wird, die es als eingebautes Programm haben.

1 Stimmen

"Immer"? Ganz und gar nicht. which Da es sich um ein externes Tool handelt, haben Sie keinen Grund zu glauben, dass es sich identisch zur übergeordneten Shell verhält.

10voto

kenorb Punkte 134883

Versuchen Sie es mit der folgenden kompatiblen Lösung:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

Da die Befehle wie realpath o readlink nicht verfügbar sein könnte (je nach Betriebssystem).

Hinweis: In der Bash wird die Verwendung von ${BASH_SOURCE[0]} anstelle von $0 , sonst kann der Pfad beim Sourcen der Datei unterbrochen werden ( source / . ).

Alternativ können Sie auch die folgende Funktion in der Bash ausprobieren:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Diese Funktion benötigt ein Argument. Wenn das Argument bereits einen absoluten Pfad enthält, wird er so gedruckt, wie er ist, andernfalls wird $PWD Variable + Argument Dateiname (ohne ./ Präfix).

Verwandt:

0 Stimmen

Bitte erklären Sie mehr über die Funktion realpath.

2 Stimmen

@Chris realpath Funktion benötigt 1 Argument. Wenn das Argument bereits einen absoluten Pfad enthält, wird er so gedruckt, wie er ist, andernfalls wird $PWD + Dateiname (ohne ./ Präfix).

2 Stimmen

Ihre cross-kompatible Lösung funktioniert nicht, wenn das Skript symlinked ist.

10voto

billyjmc Punkte 373

Ich habe viele der gegebenen Antworten verglichen und habe einige kompaktere Lösungen gefunden. Diese scheinen alle verrückten Randfälle zu behandeln, die sich aus Ihrer Lieblingskombination von ergeben:

  • Absolute Pfade oder relative Pfade
  • Datei und Verzeichnis Softlinks
  • Anrufung als script , bash script , bash -c script , source script , oder . script
  • Leerzeichen, Tabulatoren, Zeilenumbrüche, Unicode, usw. in Verzeichnissen und/oder Dateinamen
  • Dateinamen, die mit einem Bindestrich beginnen

Wenn Sie von Linux aus arbeiten, scheint es, dass die Verwendung der proc Handle ist die beste Lösung, um die vollständig aufgelöste Quelle des aktuell laufenden Skripts zu finden (in einer interaktiven Sitzung verweist der Link auf die entsprechende /dev/pts/X ):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

Das ist zwar ein wenig hässlich, aber die Lösung ist kompakt und leicht verständlich. Wir verwenden nicht nur Bash-Primitive, aber das ist in Ordnung, denn readlink vereinfacht diese Aufgabe erheblich. Die echo X fügt eine X an das Ende der Variablen-Zeichenkette, damit nachstehende Leerzeichen im Dateinamen nicht gefressen werden, und die Parametersubstitution ${VAR%X} am Ende der Zeile wird die X . Denn readlink einen eigenen Zeilenumbruch hinzufügt (der normalerweise in der Befehlssubstitution gefressen würde, wenn nicht unser vorheriger Trick angewendet worden wäre), müssen wir auch diesen loswerden. Dies lässt sich am einfachsten mit der Option $'' Zitierschema, das uns die Verwendung von Escape-Sequenzen wie \n um Zeilenumbrüche darzustellen (auf diese Weise können Sie auch leicht Verzeichnisse und Dateien mit abwegigen Namen erstellen).

Die obigen Angaben sollten Ihre Bedürfnisse abdecken, um das aktuell laufende Skript unter Linux zu finden, aber wenn Sie nicht die proc Dateisystem zur Verfügung haben, oder wenn Sie versuchen, den vollständig aufgelösten Pfad einer anderen Datei zu finden, dann finden Sie vielleicht den folgenden Code hilfreich. Es ist nur eine kleine Abwandlung des obigen Einzeilers. Wenn Sie mit seltsamen Verzeichnis-/Dateinamen herumspielen, überprüfen Sie die Ausgabe mit beiden ls y readlink ist informativ, da ls gibt "vereinfachte" Pfade aus und ersetzt ? für Dinge wie Zeilenumbrüche.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

0 Stimmen

Ich bekomme /dev/pts/30 mit bash auf Ubuntu 14.10 Desktop.

0 Stimmen

@DanDascalescu Verwenden Sie den Einzeiler? Oder den vollständigen Codeschnipsel am Ende? Und haben Sie ihm irgendwelche kniffligen Pfadnamen eingegeben?

0 Stimmen

Die eine Zeile plus eine weitere Zeile zu echo $resolved Ich habe es gespeichert als d , chmod +x d , ./d .

9voto

AsymLabs Punkte 863

Die beste kompakte Lösung wäre meiner Meinung nach:

"$( cd "$( echo "${BASH_SOURCE[0]%/*}" )"; pwd )"

Es gibt keine Abhängigkeit von etwas anderem als Bash. Die Verwendung von dirname , readlink y basename zu Kompatibilitätsproblemen führen, so dass sie am besten vermieden werden sollten, wenn dies möglich ist.

4 Stimmen

Sie sollten wahrscheinlich einen Schrägstrich hinzufügen: "$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )" . Sie würden sonst Probleme mit dem Root-Verzeichnis bekommen. Und warum müssen Sie überhaupt echo verwenden?

0 Stimmen

dirname y basename sind POSIX-standardisiert, warum sollte man sie also nicht verwenden? Links: dirname , basename

0 Stimmen

Die Vermeidung von zwei zusätzlichen Prozessgabeln und das Festhalten an Shell-Build-ins könnte ein Grund dafür sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X