Wie setze ich das aktuelle Arbeitsverzeichnis auf das Verzeichnis des Skripts?

569

Ich schreibe ein Bash-Skript. Das aktuelle Arbeitsverzeichnis muss immer das Verzeichnis sein, in dem sich das Skript befindet.

Das Standardverhalten ist, dass das aktuelle Arbeitsverzeichnis im Skript das der Shell ist, von der aus ich es ausführe, aber ich möchte dieses Verhalten nicht.

Jamesfischer
quelle
Haben Sie darüber nachgedacht, ein Wrapper-Skript wie / usr / bin auf CD in das (fest codierte) richtige Verzeichnis zu legen und dann Ihr Skript auszuführen?
Dagg Nabbit
2
Warum brauchen Sie das Verzeichnis des Skripts? Es gibt wahrscheinlich einen besseren Weg, um das zugrunde liegende Problem zu lösen.
Bstpierre
12
Ich möchte nur darauf hinweisen, dass das Verhalten, das Sie als "offensichtlich unerwünscht" bezeichnen, tatsächlich völlig notwendig ist. Wenn ich es ausführe, myscript path/to/fileerwarte ich, dass das Skript den Pfad / zur / Datei relativ zu MEINEM aktuellen Verzeichnis auswertet, nicht zu dem Verzeichnis, in dem das Skript ausgeführt wird Was wäre für ein Skript passiert, mit dem ssh remotehost bash < ./myscriptin den BASH-FAQ erwähnt wird?
Gordon Davisson
3
cd "${BASH_SOURCE%/*}" || exit
Caw

Antworten:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
quelle
7
dirname gibt 'zurück.' bei Verwendung von Bash unter Windows. Die Antwort von Paulus ist also besser.
Tvaroh
7
Gibt auch '.' Zurück. in Mac OSX
Ben Clayton
4
Es ist erwähnenswert, dass Dinge brechen können, wenn eine symbolische Verbindung einen Teil davon ausmacht $0. In Ihrem Skript können Sie beispielsweise erwarten, dass Sie ../../sich auf das Verzeichnis zwei Ebenen über dem Speicherort des Skripts beziehen. Dies ist jedoch nicht unbedingt der Fall, wenn symbolische Links im Spiel sind.
Brian Gordon
20
Wenn Sie das Skript als aufgerufen haben ./script, .ist es das richtige Verzeichnis, und wenn Sie .es ändern, wird es auch in dem Verzeichnis scriptgespeichert, in dem es sich befindet, dh im aktuellen Arbeitsverzeichnis.
ndim
6
Wenn Sie das Skript wie folgt aus dem aktuellen Verzeichnis ausführen bash script.sh, $0lautet der Wert von script.sh. Der cdBefehl "funktioniert" nur, wenn Sie sich nicht um fehlgeschlagene Befehle kümmern. Wenn Sie set -o errexit(aka set -e:) verwenden würden, um sicherzustellen, dass Ihr Skript nicht an fehlgeschlagenen Befehlen vorbeigeht, würde dies NICHT funktionieren, da cd script.shes sich um einen Fehler handelt. Der zuverlässige [Bash-spezifische] Weg istcd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky
322

Folgendes funktioniert auch:

cd "${0%/*}"

Die Syntax wird in dieser StackOverflow-Antwort ausführlich beschrieben .

Paul Schulz
quelle
17
Erklärung, wie es funktioniert: stackoverflow.com/questions/6393551/…
Kenorb
25
Vergessen Sie nicht, in Anführungszeichen zu setzen, wenn der Pfad Leerzeichen enthält. dh CD "$ {0% / *}"
H. Rabiee
17
Dies schlägt fehl, wenn das Skript mit bash script.sh- $0wird nur der Name der Datei
Chris Watts
17
Ich würde sagen, diese Antwort ist zu kurz. Sie lernen kaum etwas über die Syntax. Eine Beschreibung, wie man dies liest, wäre hilfreich.
Fraxtur
2
Dieser Trick erzeugt auch keine Unterschale und ist daher gut für Geschwindigkeitsfreaks.
Teknopaul
184

Probieren Sie die folgenden einfachen Einzeiler aus:


Für alle UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Bash

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

Hinweis: Ein doppelter Bindestrich (-) wird in Befehlen verwendet, um das Ende der Befehlsoptionen anzuzeigen, sodass Dateien mit Bindestrichen oder anderen Sonderzeichen den Befehl nicht beschädigen.

Hinweis: Verwenden Sie ${BASH_SOURCE[0]}in Bash zugunsten von $0, da sonst der Pfad bei der Beschaffung unterbrochen werden kann ( source/ .).


Für Linux, Mac und andere * BSD:

cd "$(dirname "$(realpath "$0")")";

Hinweis: realpathSollte standardmäßig in der beliebtesten Linux-Distribution (wie Ubuntu) installiert sein, in einigen Fällen kann es jedoch fehlen, sodass Sie es installieren müssen.

Hinweis: Wenn Sie Bash verwenden, verwenden Sie ${BASH_SOURCE[0]}zugunsten von $0, da sonst der Pfad bei der Beschaffung unterbrochen werden kann ( source/ .).

Andernfalls können Sie so etwas ausprobieren (es wird das erste vorhandene Tool verwendet):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Für Linux spezifisch:

cd "$(dirname "$(readlink -f "$0")")"

Verwenden des GNU-Readlinks auf * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Hinweis: Sie müssen coreutilsinstalliert haben (z. B. 1. Homebrew installieren , 2. brew install coreutils).


In Bash

In bash können Sie Parametererweiterungen verwenden , um dies zu erreichen, wie zum Beispiel:

cd "${0%/*}"

Es funktioniert jedoch nicht, wenn das Skript aus demselben Verzeichnis ausgeführt wird.

Alternativ können Sie die folgende Funktion in bash definieren:

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

Diese Funktion benötigt 1 Argument. Wenn das Argument bereits einen absoluten Pfad hat, drucken Sie es so wie es ist, andernfalls drucken Sie das $PWDArgument Variable + Dateiname (ohne ./Präfix).

oder hier ist die Version aus der Debian- .bashrcDatei:

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Verbunden:

Siehe auch:

Wie kann ich das Verhalten von GNUs readlink -f auf einem Mac ermitteln?

Kenorb
quelle
4
Eine viel bessere Antwort als die gängigen, da sie Syslinks auflöst und verschiedene Betriebssysteme berücksichtigt. Vielen Dank!
Dennis
Danke für diese Antwort. Das oberste hat auf meinem Mac funktioniert ... aber was macht der Schalter - im cpBefehl @kenorb?
TobyG
3
In --Befehlen wird ein doppelter Bindestrich ( ) verwendet, um das Ende der Befehlsoptionen anzuzeigen, damit Dateien, die Bindestriche oder andere Sonderzeichen enthalten, den Befehl nicht beschädigen. Versuchen Sie beispielsweise, die Datei über touch "-test"und zu erstellen touch -- -test, und entfernen Sie sie dann über rm "-test"und rm -- -test, und sehen Sie den Unterschied.
Kenorb
1
Ich würde meine Gegenstimme entfernen, wenn ich könnte: realpath ist veraltet unix.stackexchange.com/questions/136494/…
lsh
1
@lsh Es heißt, dass nur das Debian- realpathPaket veraltet ist, nicht GNU realpath. Wenn Sie der Meinung sind, dass dies nicht klar ist, können Sie eine Bearbeitung vorschlagen.
Kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

Es ist einfach. Es klappt.

Dan Moulding
quelle
1
Dies sollte die akzeptierte Antwort sein. Funktioniert unter OSX und Linux Ubuntu.
David Rissato Cruz
5
Dies funktioniert hervorragend, wenn das Skript B von einem anderen Skript A stammt und Sie Pfade relativ zu Skript B verwenden müssen. Vielen Dank.
Enrico
5
Dies funktioniert auch in Cygwin für diejenigen von uns, die das Pech haben, Windows berühren zu müssen.
Bruno Bronosky
3
Odercd "${BASH_SOURCE%/*}" || exit
caw
Funktioniert auf macOS Mojave
Juan Boero
6

Die akzeptierte Antwort eignet sich gut für Skripte, die an keiner anderen Stelle verknüpft wurden, z. B. in $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Wenn das Skript jedoch über einen Symlink ausgeführt wird,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Dies wird in das ~/bin/Verzeichnis und nicht in das Verzeichnis ~/project/verschoben, wodurch Ihr Skript wahrscheinlich beschädigt wird, wenn der Zweck von darin cdbesteht, Abhängigkeiten in Bezug auf einzuschließen~/project/

Die symlink sichere Antwort ist unten:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f ist erforderlich, um den absoluten Pfad der möglicherweise verknüpften Datei aufzulösen.

Die Anführungszeichen sind erforderlich, um Dateipfade zu unterstützen, die möglicherweise Leerzeichen enthalten können (schlechte Vorgehensweise, aber es ist nicht sicher anzunehmen, dass dies nicht der Fall ist).

James McGuigan
quelle
4

Dieses Skript scheint für mich zu funktionieren:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

Die pwd-Befehlszeile gibt den Speicherort des Skripts als aktuelles Arbeitsverzeichnis wieder, unabhängig davon, von wo aus ich es ausführe.

Amardeep AC9MF
quelle
3
realpathist unwahrscheinlich, überall installiert zu werden. Was je nach Situation des OP möglicherweise keine Rolle spielt.
Bstpierre
Mein System hat nicht, realpathaber es hat, readlinkwas ähnlich zu sein scheint.
Bis auf weiteres angehalten.
2
In welchem ​​dist ist standardmäßig kein realpath installiert? Ubuntu hat es.
Kenorb
1
cd "`dirname $(readlink -f ${0})`"

quelle
Können Sie bitte Ihre Antwort erklären?
Zulu
1
Obwohl dieser Code zur Lösung des Problems beitragen kann, erklärt er nicht, warum und / oder wie er die Frage beantwortet. Die Bereitstellung dieses zusätzlichen Kontextes würde den langfristigen Bildungswert erheblich verbessern. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der Einschränkungen und Annahmen.
Toby Speight
-5
echo $PWD

PWD ist eine Umgebungsvariable.

Maverick Holdem
quelle
5
Dies gibt nur das Verzeichnis an, von dem aus aufgerufen wird, nicht das Verzeichnis, in dem sich das Skript befindet.
Dagelf
-6

Wenn Sie nur das aktuelle Arbeitsverzeichnis drucken müssen, können Sie diesem folgen.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Ausführungsberechtigung erteilen:

chmod u+x test

Führen Sie dann das Skript aus, bis ./testSie das aktuelle Arbeitsverzeichnis sehen können.

Chandan
quelle
2
Die Frage war, wie sichergestellt werden kann, dass das Skript in einem eigenen Verzeichnis ausgeführt wird, auch wenn dies nicht das Arbeitsverzeichnis ist. Das ist also keine Antwort. Wie auch immer, warum tun Sie dies, anstatt nur ... zu rennen pwd? Scheint mir eine Menge verschwendeter Tastendrücke zu sein.
underscore_d