it-swarm.com.de

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 dasselbe Verzeichnis ändern, in dem sich die Skriptdatei befindet?

440
William Yeung

In Bash solltest du das bekommen, was du so brauchst:

#!/usr/bin/env bash

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

Der ursprüngliche Beitrag enthält die Lösung (ignorieren Sie die Antworten, sie fügen nichts Nützliches hinzu). Die interessante Arbeit erledigt der erwähnte Unix-Befehl readlink mit der Option -f. Funktioniert, wenn das Skript sowohl über einen absoluten als auch über einen relativen Pfad aufgerufen wird.

Für bash, sh, ksh:

#!/bin/bash 
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH

Für tcsh, csh:

#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH

Siehe auch: https://stackoverflow.com/a/246128/59087

363
al.

Ein früherer Kommentar zu einer Antwort sagte es, aber es ist leicht, unter all den anderen Antworten zu verfehlen.

Bei Verwendung von Bash:

echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"

Bash-Referenzhandbuch, 5.2 Bash-Variablen

48
Daniel

Angenommen, Sie verwenden Bash

#!/bin/bash

current_dir=$(pwd)
script_dir=$(dirname $0)

echo $current_dir
echo $script_dir

Dieses Skript sollte das Verzeichnis drucken, in dem Sie sich befinden, und dann das Verzeichnis, in dem sich das Skript befindet. Wenn Sie es beispielsweise von / mit dem Skript in /home/mez/ aufrufen, wird es ausgegeben

/
/home/mez

Denken Sie daran, dass Sie beim Zuweisen von Variablen aus der Ausgabe eines Befehls den Befehl in $( und ) einschließen müssen - sonst erhalten Sie nicht die gewünschte Ausgabe.

38
Mez

Wenn Sie Bash verwenden ...

#!/bin/bash

pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null

echo "${basedir}"
31
docwhat

Wie der Marko vorschlägt:

BASEDIR=$(dirname $0)
echo $BASEDIR

Dies funktioniert nur, wenn Sie das Skript in demselben Verzeichnis ausführen, in dem sich das Skript befindet. In diesem Fall erhalten Sie den Wert '.'

Um dieses Problem zu umgehen, verwenden Sie:

current_dir=$(pwd)
script_dir=$(dirname $0)

if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi

Sie können jetzt die Variable current_dir im gesamten Skript verwenden, um auf das Skriptverzeichnis zu verweisen. Dies kann jedoch immer noch das Symlink-Problem haben.

18
ranamalo

Die beste Antwort auf diese Frage wurde hier beantwortet:
Abrufen des Quellverzeichnisses eines Bash-Skripts von innen

Und ist:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Einzeilig, wodurch Sie den vollständigen Verzeichnisnamen des Skripts erhalten, unabhängig davon, von wo aus es aufgerufen wird.

Um zu verstehen, wie es funktioniert, können Sie das folgende Skript ausführen:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"
17
cd $(dirname $(readlink -f $0))
14
blueyed

Machen wir daraus einen POSIX-Oneliner:

a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)

Getestet auf vielen Bourne-kompatiblen Shells, einschließlich der BSD-Shells.

Soweit ich weiß, bin ich der Autor und habe es öffentlich zugänglich gemacht. Weitere Informationen finden Sie unter: https://www.jasan.tk/posts/2017-05-11-posix_Shell_dirname_replacement/

8
Ján Sáreník
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
8
searchrome

EINFÜHRUNG

Diese Antwort korrigiert die sehr kaputte, aber schockierend top bewertete Antwort dieses Threads (geschrieben von TheMarko):

#!/usr/bin/env bash

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

WARUM FUNKTIONIERT DAS VERWENDEN DES Dirnamens "$ 0" NICHT?

dirname $ 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.

Lassen Sie uns zunächst verstehen, wie diese Antwort funktioniert. Er erhält das Skriptverzeichnis, indem er tut

dirname "$0"

$ repräsentiert den ersten Teil des Befehls, der das Skript aufruft (im Grunde ist es 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. Also wenn du tust:

  dirname /usr/bin/sha256sum

sie erhalten:/usr/bin

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

  dirname "/some/path/./script"

würde nicht gut funktionieren und würde Ihnen geben:

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

Angenommen, Sie befinden sich in demselben Verzeichnis wie Ihr Skript und starten es mit diesem Befehl

./script   

$ 0 in dieser Situation ist ./script und Dirname $ 0 ergibt:

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

Verwenden von:

sh script

Ohne Eingabe des vollständigen Pfads erhalten Sie außerdem ein BASISIR = "."

Relative Verzeichnisse verwenden:

 ../some/path/./script

Gibt einen Verzeichnisnamen $ 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, erneut einen relativen Pfad):

 path/./script.sh

Sie erhalten diesen Wert für dirname $ 0:

 path/. 

und ./path/./script (eine andere Form des relativen Pfades) gibt an:

 ./path/.

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

 $0=/some/path/script

das gibt Ihnen einen Pfad, den Sie mit dirname verwenden können.

DIE LÖSUNG

Sie müssten jede der oben genannten Situationen berücksichtigen und erkennen und eine Korrektur dafür vornehmen, wenn dies der Fall ist:

#!/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
4
thebunnyrules

Wenn Sie das aktuelle Skriptverzeichnis abrufen möchten (unabhängig davon, ob Sie das Skript über einen Symlink oder direkt aufrufen), versuchen Sie Folgendes:

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

Dies funktioniert sowohl unter Linux als auch unter MacOS. Ich konnte hier niemanden sehen, der etwas über realpath erwähnte. Nicht sicher, ob dieser Ansatz Nachteile hat.

unter MacOS müssen Sie coreutils installieren, um realpath zu verwenden. ZB: brew install coreutils.

3
Rohith

Dieser Einzeiler gibt an, wo sich das Shell-Skript befindet spielt keine Rolle, ob Sie es ausgeführt haben oder ob Sie es bezogen haben. Außerdem werden alle beteiligten symbolischen Links aufgelöst, wenn 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.

3
Richard Gomes

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 systemübergreifend und auf allen bash-Systemen zu funktionieren (keine Annahmen zu bash-Versionen oder readlink- oder pwd -Optionen) und vernünftigerweise das tut, was Sie möchten Erwarten Sie, dass dies passiert (z. B. das Auflösen von Symlinks ist ein interessantes Problem, aber normalerweise nicht das, was Sie wirklich wollen), behandeln Sie Edge-Fälle wie Leerzeichen in Pfaden usw., ignorieren Sie alle Fehler und verwenden Sie einen vernünftigen Standard, wenn es Probleme gibt.

Jede Komponente wird in einer separaten Variablen 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)"
2
michael

Inspiriert von blueyeds Antwort

read < <(readlink -f $0 | xargs dirname)
cd $REPLY
2
Steven Penny