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

26voto

lamawithonel Punkte 21

Hier ist ein POSIX-kompatibler Einzeiler:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

5 Stimmen

Ich hatte damit Erfolg, wenn ich ein Skript selbst oder mit sudo ausgeführt habe, aber nicht, wenn ich die Quelle ./script.sh aufrief

0 Stimmen

Und es scheitert, wenn cd ist so konfiguriert, dass der neue Pfadname gedruckt wird.

23voto

Gabriel Staples Punkte 20228

Zusammenfassung:

FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"

# OR, if you do NOT need it to work for **sourced** scripts too:
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"

# OR, depending on which path you want, in case of nested `source` calls
# FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"

# OR, add `-s` to NOT expand symlinks in the path:
# FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

Einzelheiten:

Wie erhält man die vollständiger Dateipfad , vollständiges Verzeichnis y Basisdateiname eines beliebigen Skripts laufen. OR bezogen ...

...auch wenn das aufgerufene Skript aus einer anderen Bash-Funktion oder einem Skript heraus aufgerufen wird, oder wenn verschachteltes Sourcing verwendet wird!

In vielen Fällen brauchen Sie nur den vollständigen Pfad zu dem Skript, das Sie gerade aufgerufen haben. Dies kann leicht erreicht werden mit realpath . Beachten Sie, dass realpath ist Teil von GNU coreutils . Wenn Sie es nicht bereits installiert haben (bei Ubuntu ist es standardmäßig installiert), können Sie es mit sudo apt update && sudo apt install coreutils .

get_script_path.sh (die neueste Version dieses Skripts finden Sie unter get_script_path.sh in meinem eRCaGuy_hello_world repo):

#!/bin/bash

# A. Obtain the full path, and expand (walk down) symbolic links
# A.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# A.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# B.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "$0")"
# B.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "${BASH_SOURCE[-1]}")"

# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY    = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME     = \"$SCRIPT_FILENAME\""

WICHTIGER Hinweis zu verschachtelt source ruft auf. : wenn "${BASH_SOURCE[-1]}" nicht das gewünschte Ergebnis liefert, versuchen Sie es mit "${BASH_SOURCE[0]}" stattdessen. Die erste ( 0 ) gibt Ihnen den ersten Eintrag im Array, und der letzte ( -1 ) gibt Ihnen den letzten Eintrag im Array. Je nachdem, wonach Sie suchen, möchten Sie vielleicht den ersten Eintrag. Ich entdeckte, dass dies der Fall war, als ich ~/.bashrc con . ~/.bashrc die aus dem ~/.bash_aliases con . ~/.bash_aliases und ich wollte die realpath (mit erweiterten Symlinks) zum ~/.bash_aliases Datei, NICHT in die ~/.bashrc Datei. Da es sich um verschachtelt source Anrufe, mit "${BASH_SOURCE[0]}" gab mir, was ich wollte: den erweiterten Weg zu ~/.bash_aliases ! Verwendung von "${BASH_SOURCE[-1]}" gab mir jedoch, was ich tat ノット wollen: den erweiterten Pfad zu ~/.bashrc .

Beispielbefehl und -ausgabe:

  1. Laufen das Drehbuch:

    ~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh 
    FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
    SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
    SCRIPT_FILENAME     = "get_script_path.sh"
  2. Beschaffung das Skript mit . get_script_path.sh o source get_script_path.sh (das Ergebnis ist genau dasselbe wie oben, weil ich die "${BASH_SOURCE[-1]}" im Skript anstelle von "$0" ):

    ~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh 
    FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
    SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
    SCRIPT_FILENAME     = "get_script_path.sh"

Wenn Sie "$0" im Skript anstelle von "${BASH_SOURCE[-1]}" erhalten Sie die gleiche Ausgabe wie oben, wenn läuft das Skript, aber das Unerwünschte ausgegeben, wenn Beschaffung das Drehbuch:

~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh 
FULL_PATH_TO_SCRIPT               = "/bin/bash"
SCRIPT_DIRECTORY                  = "/bin"
SCRIPT_FILENAME                   = "bash"

Und, wenn Sie die "$BASH_SOURCE" anstelle von "${BASH_SOURCE[-1]}" wird es ノット funktionieren, wenn das Skript aus einer anderen Bash-Funktion heraus aufgerufen wird. Also, mit "${BASH_SOURCE[-1]}" ist daher der beste Weg, da er diese beiden Probleme löst! Siehe die Referenzen unten.

Der Unterschied zwischen realpath y realpath -s :

Beachten Sie, dass realpath geht auch erfolgreich symbolische Links ab, um ihre Ziele zu bestimmen und auf sie zu zeigen, anstatt auf den symbolischen Link zu zeigen. Wenn Sie dieses Verhalten NICHT wollen (manchmal will ich das nicht), dann fügen Sie -s zum realpath Befehl, so dass die Zeile stattdessen wie folgt aussieht:

# Obtain the full path, but do NOT expand (walk down) symbolic links; in
# other words: **keep** the symlinks as part of the path!
FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

Auf diese Weise werden symbolische Links NICHT erweitert. Sie werden vielmehr so belassen, wie sie sind, als symbolische Links im vollständigen Pfad.

Der obige Code ist jetzt Teil meiner eRCaGuy_hello_world Repo in dieser Datei hier: bash/get_script_path.sh . Verweisen Sie auf diese Datei und führen Sie sie aus, um vollständige Beispiele sowohl mit als auch ohne Symlinks in den Pfaden zu erhalten. Am Ende der Datei finden Sie Beispielausgaben für beide Fälle.

Referenzen:

  1. Abrufen des absoluten Pfads bei relativem Pfad
  2. lehrte mich über die BASH_SOURCE variabel: Unix & Linux: Ermitteln des Pfads zum gesourcten Shell-Skript
  3. lehrte mich, dass BASH_SOURCE ist eigentlich ein Array, und wir wollen das letzte Element daraus, damit es innerhalb einer Funktion wie erwartet funktioniert (deshalb habe ich "${BASH_SOURCE[-1]}" in meinem Code hier): Unix & Linux: Ermittlung des Pfades zum gesourcten Shell-Skript
  4. man bash --> Suche nach BASH_SOURCE :

    BASH_SOURCE

    Eine Array-Variable, deren Mitglieder die Namen der Quelldateien sind, in denen die entsprechenden Shell-Funktionsnamen in der FUNCNAME Array-Variable definiert sind. Die Shell-Funktion ${FUNCNAME[$i]} ist definiert in der Datei ${BASH_SOURCE[$i]} und aufgerufen von ${BASH_SOURCE[$i+1]} .

Siehe auch:

  1. (meine Antwort) Unix & Linux: Ermitteln des Pfads zum gesourcten Shell-Skript

2 Stimmen

Was ist der Unterschied zwischen ${BASH_SOURCE[-1]} y ${BASH_SOURCE[0]} ? Ich weiß -1 holt das letzte Element aus dem Array und 0 ruft den ersten ab, aber in welchem Fall sollte ich den einen vor dem anderen verwenden?

0 Stimmen

@ElieG., Siehe meine IMPORTANT note on nested source calls Abschnitt in der Antwort. Es hat mit verschachteltem Sourcing zu tun, wenn ein Skript, das Sie sourcen, ein anderes Skript sourced.

0 Stimmen

Entschuldigung, diesen Teil habe ich übersehen. Wenn ich also richtig verstehe, -1 gibt das übergeordnete Skript und 0 gibt das aktuelle Skript?

21voto

#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

1 Stimmen

Ich habe es nicht auf verschiedenen Systemen getestet. Aber diese Lösung ist diejenige, die zumindest unter Ubuntu sofort funktioniert, für mich!

20voto

DarkPark Punkte 1

Versuchen Sie es mit:

real=$(realpath "$(dirname "$0")")

1 Stimmen

Ich möchte nur wissen, warum dieser Weg nicht gut ist. Für mich schien er nicht schlecht und richtig zu sein. Könnte jemand erklären, warum es heruntergestuft wird?

7 Stimmen

Realpath ist kein Standardprogramm.

5 Stimmen

Unter Linux ist realpath ein Standarddienstprogramm (Teil des GNU coreutils-Pakets), aber es ist kein Bash-Integral (d.h. eine Funktion, die von der Bash selbst bereitgestellt wird). Wenn Sie Linux benutzen, wird diese Methode wahrscheinlich funktionieren, obwohl ich die $0 para ${BASH_SOURCE[0]} so dass diese Methode überall funktioniert, auch in einer Funktion.

19voto

Ich habe sie alle ausprobiert, aber keine hat funktioniert. Eines war sehr nahe dran, aber es hatte einen kleinen Fehler, der es kaputt machte; sie vergaßen, den Pfad in Anführungszeichen zu setzen.

Außerdem gehen viele Leute davon aus, dass Sie das Skript von einer Shell aus starten, und vergessen dabei, dass beim Öffnen eines neuen Skripts standardmäßig Ihr Heimatverzeichnis verwendet wird.

Probieren Sie dieses Verzeichnis einmal aus:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

Damit wird es richtig gemacht, egal wie oder wo Sie es ausführen:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Um es also wirklich nützlich zu machen, wechseln Sie in das Verzeichnis des laufenden Skripts:

cd "`dirname "$0"`"

4 Stimmen

Funktioniert nicht, wenn das Skript von einem anderen Skript bezogen wird.

0 Stimmen

Dies funktioniert nicht, wenn der letzte Teil von $0 ein symbolischer Link ist, der auf einen Eintrag in einem anderen Verzeichnis zeigt ( ln -s ../bin64/foo /usr/bin/foo ).

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