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

5voto

BillTorpey Punkte 861

Das ist die einzige Möglichkeit, die ich gefunden habe, um das zuverlässig festzustellen:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))

6 Stimmen

Ich erhalte das Verzeichnis ohne den letzten Eintrag, d. h. den Pfad zum Container des Skript-Containers.

5voto

Binary Phile Punkte 2401

Die meisten Antworten gehen entweder nicht mit Dateien um, die über einen relativen Pfad symverknüpft sind, sind keine Einzeiler oder gehen nicht mit BSD (Mac) um. Eine Lösung, die alle drei Punkte erfüllt, ist:

HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)

Wechseln Sie zunächst in das Verzeichnis, das die Bash für das Skript vorgesehen hat. Dann lesen Sie einen Link auf die Datei, um zu sehen, ob es sich um einen Symlink handelt (relativ oder anderweitig), und wenn ja, cd in dieses Verzeichnis. Ist dies nicht der Fall, wechseln Sie mit cd in das aktuelle Verzeichnis (dies ist notwendig, damit das Skript einzeilig ist). Geben Sie dann das aktuelle Verzeichnis mit echo pwd .

Sie könnten hinzufügen -- zu den Argumenten von cd und readlink hinzufügen, um Probleme mit Verzeichnissen zu vermeiden, die wie Optionen benannt sind, aber für die meisten Zwecke mache ich mir nicht die Mühe.

Die vollständige Erklärung mit Abbildungen finden Sie hier:

https://www.binaryphile.com/bash/2020/01/12/determining-the-location-of-your-script-in-bash.html

2 Stimmen

Funktioniert prima! Sowohl unter Linux als auch unter Macos getestet. Hinzugefügt, um unit-test gist mit verschiedenen anderen Lösungen: gist.github.com/ptc-mrucci/

4voto

Ein Vorteil dieser Methode ist, dass sie nichts außerhalb der Bash selbst benötigt und auch keine Subshell aufspaltet.

Verwenden Sie zunächst die Mustersubstitution, um alles zu ersetzen, was nicht mit / (d.h. ein relativer Pfad) mit $PWD/ . Da wir eine Substitution verwenden, um mit dem ersten Zeichen von $0 müssen wir es auch wieder anhängen ( ${0:0:1} in der Substitution).

Jetzt haben wir einen vollständigen Pfad zu dem Skript; wir können das Verzeichnis erhalten, indem wir das letzte / und alles, was darauf folgt (d. h. der Skriptname). Dieses Verzeichnis kann dann in cd oder als Präfix für andere Pfade relativ zu Ihrem Skript.

#!/bin/bash

BIN=${0/#[!\/]/"$PWD/${0:0:1}"}
DIR=${BIN%/*}

cd "$DIR"

Wenn Ihr Skript nicht ausgeführt, sondern gesourced werden soll, können Sie natürlich auch $0 con ${BASH_SOURCE[0]} wie zum Beispiel:

BIN=${BASH_SOURCE[0]/#[!\/]/"$PWD/${BASH_SOURCE[0]:0:1}"}

Dies funktioniert auch bei ausführbaren Skripten. Es ist länger, aber vielseitiger.

4voto

puchu Punkte 2963

$0 ist keine zuverlässige Methode, um den aktuellen Skriptpfad zu ermitteln. Zum Beispiel ist dies mein .xprofile :

#!/bin/bash
echo "$0 $1 $2"
echo "${BASH_SOURCE[0]}"
# $dir/my_script.sh &

cd /tmp && ~/.xprofile && source ~/.xprofile

/home/puchuu/.xprofile
/home/puchuu/.xprofile
-bash
/home/puchuu/.xprofile

Verwenden Sie also bitte BASH_SOURCE stattdessen.

4voto

Chaim Leib Halbert Punkte 1804

Hier ist ein Befehl, der sowohl unter Bash als auch unter zsh funktioniert, und zwar unabhängig davon, ob er eigenständig oder als Quellcode ausgeführt wird:

[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
    || this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")

Wie es funktioniert

Die aktuelle Dateierweiterung von zsh: ${(%):-%x}

${(%):-%x} in zsh expandiert zum Pfad der gerade ausgeführten Datei.

Der Fallback-Substitutionsoperator :-

Sie wissen bereits, dass ${...} ersetzt Variablen innerhalb von Zeichenketten. Sie wissen vielleicht nicht, dass bestimmte Operationen möglich sind (sowohl in Bash y zsh ) auf die Variablen während der Substitution, wie der Fallback-Expansionsoperator :- :

% x=ok
% echo "${x}"
ok

% echo "${x:-fallback}"
ok

% x=
% echo "${x:-fallback}"
fallback

% y=yvalue
% echo "${x:-$y}"
yvalue

Le site %x Prompt-Escape-Code

Als nächstes werden wir Prompt-Escape-Codes einführen, eine Funktion, die es nur in zsh gibt. In zsh, %x wird auf den Pfad der Datei erweitert, aber normalerweise ist dies nur bei der Erweiterung für Prompt-Zeichenfolgen . Um diese Codes in unserer Ersetzung zu aktivieren, können wir ein (%) Flagge vor dem Variablennamen:

% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"

% source apath/test.sh
apath/test.sh

% cd apath
% source test.sh
test.sh

Eine unwahrscheinliche Übereinstimmung: die prozentuale Flucht und der Fallback

Was wir bisher haben, funktioniert, aber es wäre ordentlicher, wenn wir die zusätzlichen fpath variabel. Anstatt die %x en fpath können wir verwenden :- und setzen %x in der Fallback-Zeichenkette:

% cat test.sh
echo "${(%):-%x}"

% source test.sh
test.sh

Beachten Sie, dass wir normalerweise einen Variablennamen zwischen (%) y :- aber wir haben es leer gelassen. Die Variable mit einem leeren Namen kann nicht deklariert oder gesetzt werden, so dass der Fallback immer ausgelöst wird.

Zum Abschluss: Was ist mit print -P %x ?

Jetzt haben wir fast das Verzeichnis unseres Skripts. Wir hätten auch print -P %x um denselben Dateipfad mit weniger Hacks zu erhalten, aber in unserem Fall, wo wir ihn als Argument an dirname Das hätte den Aufwand erfordert, eine neue Unterschale zu starten:

% cat apath/test.sh
dirname "$(print -P %x)"  # $(...) runs a command in a new process
dirname "${(%):-%x}"

% source apath/test.sh
apath
apath

Es hat sich herausgestellt, dass der umständliche Weg sowohl leistungsfähiger als auch prägnanter ist.

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