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

3voto

gkb0986 Punkte 2749

Diese Lösung gilt nur für die Bash. Beachten Sie, dass die üblicherweise gelieferte Antwort ${BASH_SOURCE[0]} wird nicht funktionieren, wenn Sie versuchen, den Pfad innerhalb einer Funktion zu finden.

Ich habe festgestellt, dass diese Zeile immer funktioniert, unabhängig davon, ob die Datei ausgelagert oder als Skript ausgeführt wird.

dirname ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

Wenn Sie Symlinks folgen wollen, verwenden Sie readlink auf dem Pfad, den Sie oben erhalten haben, rekursiv oder nicht rekursiv.

Hier ist ein Skript, um es auszuprobieren und mit anderen vorgeschlagenen Lösungen zu vergleichen. Rufen Sie es auf als source test1/test2/test_script.sh o bash test1/test2/test_script.sh .

#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside

#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

Der Grund, warum der Einzeiler funktioniert, liegt in der Verwendung der BASH_SOURCE Umgebungsvariable und ihre zugehörige FUNCNAME .

BASH_SOURCE

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

FUNCNAME

Eine Array-Variable, die die Namen aller Shell-Funktionen enthält, die sich derzeit im Aufrufstapel der Ausführung befinden. Das Element mit dem Index 0 ist der Name einer beliebigen aktuell ausgeführten Shell-Funktion. Das unterste Element (das Element mit dem höchsten Index) ist "main". Diese Variable existiert nur, wenn eine Shell-Funktion ausgeführt wird. Zuweisungen an FUNCNAME haben keine Wirkung und geben einen Fehlerstatus zurück. Wenn FUNCNAME nicht gesetzt wird, verliert sie ihre besonderen Eigenschaften, auch wenn sie anschließend zurückgesetzt wird.

Diese Variable kann mit BASH_LINENO und BASH_SOURCE verwendet werden. Jedes Element von FUNCNAME hat entsprechende Elemente in BASH_LINENO und BASH_SOURCE, um den Aufrufstapel zu beschreiben. Zum Beispiel wurde ${FUNCNAME[$i]} aus der Datei ${BASH_SOURCE[$i+1]} in Zeile ${BASH_LINENO[$i]} aufgerufen. Das Caller-Builtin zeigt den aktuellen Aufrufstapel anhand dieser Informationen an.

[Quelle: Bash-Handbuch]

3voto

Brad Parks Punkte 59613

Das folgende Beispiel gibt das aktuelle Verzeichnis des Skripts zurück

  • funktioniert mit oder ohne Quellenangabe
  • funktioniert, wenn es im aktuellen Verzeichnis oder in einem anderen Verzeichnis ausgeführt wird.
  • funktioniert, wenn relative Verzeichnisse verwendet werden.
  • funktioniert mit Bash, bei anderen Shells bin ich mir nicht sicher.

    /tmp/a/b/c $ . ./test.sh /tmp/a/b/c

    /tmp/a/b/c $ . /tmp/a/b/c/test.sh /tmp/a/b/c

    /tmp/a/b/c $ ./test.sh /tmp/a/b/c

    /tmp/a/b/c $ /tmp/a/b/c/test.sh /tmp/a/b/c

    /tmp/a/b/c $ cd

    ~ $ . /tmp/a/b/c/test.sh /tmp/a/b/c

    ~ $ . ../../tmp/a/b/c/test.sh /tmp/a/b/c

    ~ $ /tmp/a/b/c/test.sh /tmp/a/b/c

    ~ $ ../../tmp/a/b/c/test.sh /tmp/a/b/c

test.sh

#!/usr/bin/env bash

# snagged from: https://stackoverflow.com/a/51264222/26510
function toAbsPath {
    local target
    target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

function getScriptDir(){
  local SOURCED
  local RESULT
  (return 0 2>/dev/null) && SOURCED=1 || SOURCED=0

  if [ "$SOURCED" == "1" ]
  then
    RESULT=$(dirname "$1")
  else
    RESULT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  fi
  toAbsPath "$RESULT"
}

SCRIPT_DIR=$(getScriptDir "$0")
echo "$SCRIPT_DIR"

2voto

Domi Punkte 18235

Python wurde ein paar Mal erwähnt. Hier ist das JavaScript (d.h., Node.js ) Alternative:

baseDirRelative=$(dirname "$0")
baseDir=$(node -e "console.log(require('path').resolve('$baseDirRelative'))") # Get absolute path using Node.js

echo $baseDir

2voto

ghchoi Punkte 4195

Ich habe folgendes mit 3 verschiedenen Ausführungen versucht.

echo $(realpath $_)

. application         # /correct/path/to/dir or /path/to/temporary_dir
bash application      # /path/to/bash
/PATH/TO/application  # /correct/path/to/dir

echo $(realpath $(dirname $0))

. application         # failed with `realpath: missing operand`
bash application      # /correct/path/to/dir
/PATH/TO/application  # /correct/path/to/dir

echo $(realpath $BASH_SOURCE)

$BASH_SOURCE ist im Grunde dasselbe mit ${BASH_SOURCE[0]} .

. application         # /correct/path/to/dir
bash application      # /correct/path/to/dir
/PATH/TO/application  # /correct/path/to/dir

Nur $(realpath $BASH_SOURCE) scheint zuverlässig zu sein.

2voto

Rawstring Punkte 67

So gehe ich bei meinen Skripten vor:

pathvar="$( cd "$( dirname $0 )" && pwd )"

So erfahren Sie, von welchem Verzeichnis aus der Launcher (das aktuelle Skript) ausgeführt wird.

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