680 Stimmen

Unix-Shell-Skript herausfinden, in welchem Verzeichnis sich die Skriptdatei befindet?

Grundsätzlich muss ich das Skript mit Pfaden ausführen, die sich auf den Speicherort der Shell-Skriptdatei beziehen. Wie kann ich das aktuelle Verzeichnis in das gleiche Verzeichnis ändern, in dem sich die Skriptdatei befindet?

15 Stimmen

Ist das wirklich ein Duplikat? In dieser Frage geht es um ein "Unix-Shell-Skript", in der anderen speziell um Bash.

3 Stimmen

@BoltClock: Diese Frage wurde fälschlicherweise geschlossen. In der verlinkten Frage geht es um Bash. In dieser Frage geht es um Unix-Shell-Programmierung. Beachten Sie, dass die akzeptierten Antworten sehr unterschiedlich sind!

0 Stimmen

@Dietrich Epp: Sie haben Recht. Es scheint, dass die Wahl der akzeptierten Antwort durch den Fragesteller und die Hinzufügung des [bash]-Tags (wahrscheinlich als Reaktion darauf) mich dazu veranlasst hat, die Frage als Antwort auf eine Markierung als Duplikat zu kennzeichnen.

17voto

searchrome Punkte 309
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"

4 Stimmen

Die zuverlässigste nicht-basisspezifische Methode, die ich kenne.

9voto

thebunnyrules Punkte 1340

EINFÜHRUNG

Diese Antwort berichtigt die sehr kaputte, aber schockierenderweise meistgewählte Antwort dieses Threads (geschrieben von TheMarko):

#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
echo "$BASEDIR"

WARUM FUNKTIONIERT DIE VERWENDUNG des Dirnamens "$0" ALS SELBSTVERSTÄNDLICHES NICHT?

Verzeichnisname $0 funktioniert nur, wenn der Benutzer das Skript auf eine ganz bestimmte Weise startet. Ich konnte mehrere Situationen finden, in denen diese Antwort fehlschlägt und das Skript abstürzt.

Zunächst einmal sollten wir verstehen, wie diese Antwort funktioniert. Er erhält das Skriptverzeichnis, indem er

dirname "$0"

$0 steht für den ersten Teil des Befehls, der das Skript aufruft (es ist im Grunde der eingegebene Befehl ohne die Argumente:

/some/path/./script argument1 argument2

$0="/some/path/./script"

dirname findet grundsätzlich das letzte / in einer Zeichenkette und schneidet es dort ab. Wenn Sie also tun:

  dirname /usr/bin/sha256sum

erhalten Sie: /usr/bin

Dieses Beispiel funktioniert gut, weil /usr/bin/sha256sum ein korrekt formatierter Pfad ist, aber

  dirname "/some/path/./script"

würde nicht gut funktionieren und zu Problemen führen:

  BASENAME="/some/path/." #which would crash your script if you try to use it as a path

Angenommen, Sie befinden sich im gleichen Verzeichnis wie Ihr Skript und starten es mit folgendem Befehl

./script   

$0 ist in diesem Fall ./script und dirname $0 ergibt:

. #or BASEDIR=".", again this will crash your script

Verwenden:

sh script

Ohne Eingabe des vollständigen Pfades wird auch BASEDIR="." ausgegeben.

Verwendung relativer Verzeichnisse:

 ../some/path/./script

Gibt einen Dirnamen $0 von:

 ../some/path/.

Wenn Sie sich im Verzeichnis /some befinden und das Skript auf diese Weise aufrufen (beachten Sie das Fehlen von / am Anfang, wieder ein relativer Pfad):

 path/./script.sh

Sie erhalten diesen Wert für dirname $0:

 path/. 

und ./path/./script (eine andere Form des relativen Pfads) ergibt:

 ./path/.

Die einzigen beiden Situationen, in denen basedir $0 funktioniert, wenn der Benutzer sh oder touch verwendet, um ein Skript zu starten, da beide zu $0 führen:

 $0=/some/path/script

was Ihnen einen Pfad liefert, den Sie mit dirname verwenden können.

DIE LÖSUNG

Sie müssen jede der oben genannten Situationen berücksichtigen und erkennen und eine Lösung für sie finden, wenn sie auftritt:

#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.

#set to false to not see all the echos
debug=true

if [ "$debug" = true ]; then echo "\$0=$0";fi

#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
                                                                     #situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
                                                                     #situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi                                 

if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1

_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then  #fix for situation3 #<- bash only
        if [ "$B_2" = "./" ]; then
                #covers ./relative_path/(./)script
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
        else
                #covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
                if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
        fi
fi

if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi

1 Stimmen

Ich würde sagen, "sehr kaputt" ist eine schockierende Übertreibung. Ja, Sie haben den Weg gefunden, wie Sie das Drehbuch genannt haben. Was ist damit? Es sollte in dem Kontext, in dem Sie es aufgerufen haben, immer noch korrekt sein. Das ist wahrscheinlich das, was dich am meisten interessiert aber wenn du eine Art kanonisierte Version mit absolutem Pfad brauchst, weil du es z.B. an PATH dann würden Sie einfach cd in den Ordner und erhalten pwd . Aber die übliche Notwendigkeit, den Pfad zu erhalten, ich denke, ist in der Lage sein, andere Skripte relativ zum Pfad des aktuellen aufrufen, in dem ich nicht sehen, wie einer dieser Fälle keine Probleme verursachen könnte.

0 Stimmen

...abgesehen von Symlinks natürlich. Sie zu unterstützen und gleichzeitig den tatsächlichen Ort des Skripts aufzulösen, ist etwas schwieriger. Aber Ihre Hauptbeschwerden oben haben Symlinks nicht erwähnt.

7voto

Richard Gomes Punkte 5140

Dieser Einzeiler gibt an, wo sich das Shell-Skript befindet, Es spielt keine Rolle, ob Sie es betrieben haben oder ob Sie es bezogen haben. . Außerdem löst es alle beteiligten symbolischen Links auf, falls dies der Fall ist:

dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))

Übrigens, ich nehme an, Sie verwenden /bin/bash .

5voto

anton_rh Punkte 6967

Grundversion:

dir=$(dirname $0)

Wenn das Skript möglicherweise über $PATH , dann:

dir=$(dirname $(which $0))

Wenn das Skript so aufgerufen werden könnte: bash script.sh , dann:

dir=$(dirname $(which $0 2>/dev/null || realpath ./$0))

Wenn Sie sich wahnsinnig unsicher fühlen, dann:

dir="$(dirname -- "$(which -- "$0" 2>/dev/null || realpath -- "./$0")")"

2voto

michael Punkte 7677

So viele Antworten, alle plausibel, jede mit Vor- und Nachteilen und leicht unterschiedlichen Zielen (die wahrscheinlich für jede angegeben werden sollten). Hier ist eine weitere Lösung, die das primäre Ziel erfüllt, sowohl klar zu sein als auch über alle Systeme hinweg zu funktionieren, auf allen Bash-Systemen (keine Annahmen über Bash-Versionen, oder readlink o pwd Optionen), und tut vernünftigerweise das, was man erwartet (z.B. ist das Auflösen von Symlinks ein interessantes Problem, aber normalerweise nicht das, was man eigentlich will), behandelt Randfälle wie Leerzeichen in Pfaden usw., ignoriert alle Fehler und verwendet einen vernünftigen Standard, wenn es irgendwelche Probleme gibt.

Jede Komponente wird in einer separaten Variable gespeichert, die Sie einzeln verwenden können:

# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]}      # this script's name
PROG_NAME=${PROG_PATH##*/}       # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"

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