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?
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?
Antworten:
In Bash sollten Sie das bekommen, was Sie brauchen:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
auch verwenden (siehe Antwort von al unten)
$BASH_SOURCE
anstelle von zu verwenden $0
, da $0
es nicht immer den Pfad des aufgerufenen Skripts enthält, z. B. beim "Sourcing" eines Skripts.
$BASH_SOURCE
ist Bash-spezifisch, die Frage betrifft das Shell-Skript im Allgemeinen.
CUR_PATH=$(pwd)
oder pwd
geben Sie das aktuelle Verzeichnis zurück (das nicht das übergeordnete Verzeichnis des Skripts sein muss)!
$BASH_SOURCE
und sie gibt das zurück, was ich brauchte. Mein Skript wird von einem anderen Skript aufgerufen und gibt $0
zurück, .
während $BASH_SOURCE
das richtige Unterverzeichnis (in meinem Fall scripts
) zurückgegeben wird.
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 Option -f
. Funktioniert, wenn das Skript sowohl von einem absoluten als auch von einem 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 gilt 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
readlink
. Aus diesem Grund habe ich die Verwendung von pushd / popd (integrierte Funktionen für Bash) empfohlen.
-f
Option, readlink
unter OS X (Lion) und möglicherweise BSD etwas anderes zu tun. stackoverflow.com/questions/1055671/…
-f
OS X (ab Lion) überhaupt nicht unterstützt. Dort können Sie entweder das ablegen -f
, um mit dem Auflösen höchstens einer Indirektionsebene auszukommen, z. B. pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
oder Sie können Ihr eigenes rekursives Symlink-Follow-Skript erstellen, wie im verlinkten Beitrag gezeigt.
sh /some/other/directory/script.sh)
in diesem Fall .
Ihr pwd, nicht/some/other/directory
Ein früherer Kommentar zu einer Antwort sagte es, aber es ist leicht, unter allen anderen Antworten zu übersehen.
Bei Verwendung von Bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
stattdessen Leerzeichen in $ BASH_SOURCE verwenden.
"$(dirname "${BASH_SOURCE[0]}")"
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 /
mit dem Skript aufrufen /home/mez/
, wird es ausgegeben
/
/home/mez
Denken Sie daran, wenn Sie Variablen aus der Ausgabe eines Befehls zuweisen, schließen Sie den Befehl ein $(
und )
- oder Sie erhalten nicht die gewünschte Ausgabe.
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}"
pushd
/ popd
durch cd $(dirname "${0}")
und ersetzen cd -
, damit es auf anderen Shells funktioniert, wenn diese eine haben pwd -L
.
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 in Ihrem Skript verwenden, um auf das Skriptverzeichnis zu verweisen. Dies kann jedoch immer noch das Symlink-Problem haben.
Die beste Antwort auf diese Frage wurde hier beantwortet:
Abrufen des Quellverzeichnisses eines Bash-Skripts von innen
Und es ist:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
Einzeiler, der Ihnen den vollständigen Verzeichnisnamen des Skripts gibt, unabhängig davon, von wo 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'"
cd $(dirname $(readlink -f $0))
Machen wir es zu einem POSIX-Oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Getestet auf vielen Bourne-kompatiblen Schalen, einschließlich der BSD-Schalen.
Soweit ich weiß, bin ich der Autor und habe es gemeinfrei gemacht. Weitere Informationen finden Sie unter: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
wenn Leerzeichen im Pfad, und kehrt zurück $PWD
. (eine offensichtliche Lösung, zeigt aber nur, wie viele
Wenn Sie das eigentliche 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 erwähnen sehen realpath
. Ich bin mir nicht sicher, ob dieser Ansatz Nachteile aufweist.
Unter MacOS müssen Sie installieren, coreutils
um verwenden zu können realpath
. ZB : brew install coreutils
.
EINFÜHRUNG
Diese Antwort korrigiert die sehr kaputte, aber schockierend am besten gewählte Antwort dieses Threads (geschrieben von TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
WARUM FUNKTIONIERT DIE VERWENDUNG 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 zum Absturz bringt.
Lassen Sie uns zunächst verstehen, wie diese Antwort funktioniert. Er bekommt das Skriptverzeichnis, indem er es tut
dirname "$0"
$ 0 stellt den ersten Teil des Befehls dar, 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 im Grunde das letzte / in einem String und schneidet es dort ab. Wenn Sie dies tun:
dirname /usr/bin/sha256sum
Sie erhalten: / usr / bin
Dieses Beispiel funktioniert gut, da / usr / bin / sha256sum aber ein richtig formatierter Pfad ist
dirname "/some/path/./script"
würde nicht gut funktionieren und würde dir geben:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Angenommen, Sie befinden sich im selben Verzeichnis wie Ihr Skript und starten es mit diesem Befehl
./script
$ 0 in dieser Situation ist ./script und dirname $ 0 gibt an:
. #or BASEDIR=".", again this will crash your script
Verwenden von:
sh script
Ohne Eingabe des vollständigen Pfads wird auch ein BASEDIR = "."
Relative Verzeichnisse verwenden:
../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 Pfades) ergibt:
./path/.
Die einzigen zwei Situationen, in denen basedir $ 0 funktioniert, sind, wenn der Benutzer sh oder touch verwendet, um ein Skript zu starten, da beide zu $ 0 führen:
$0=/some/path/script
Dadurch erhalten Sie einen Pfad, den Sie mit dirname verwenden können.
DIE LÖSUNG
Sie müssen jede der oben genannten Situationen berücksichtigen und erkennen und eine Korrektur dafür anwenden, 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
Dieser Einzeiler gibt an, wo sich das Shell-Skript befindet. Dabei spielt es keine Rolle, ob Sie es ausgeführt haben oder ob Sie es bezogen haben . Außerdem werden alle beteiligten symbolischen Verknüpfungen aufgelöst, wenn dies der Fall ist:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
Ich nehme an, Sie verwenden / bin / bash .
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 Hauptziel erfüllt, klar zu sein und systemübergreifend auf allen Bashs zu arbeiten (keine Annahmen über Bash-Versionen oderreadlink
oderpwd
-Optionen) und vernünftigerweise das zu tun, was Sie erwarten würden (z. B. das Auflösen von Symlinks ist eine interessantes Problem, aber normalerweise nicht das, was Sie tatsächlich wollen), behandeln Sie Randfä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)"
Inspiriert von Blueyeds Antwort
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Das sollte den Trick machen:
echo `pwd`/`dirname $0`
Es könnte hässlich aussehen, abhängig davon, wie es aufgerufen wurde und wie es mit dem CWD funktioniert, aber es sollte Sie dahin bringen, wo Sie hin müssen (oder Sie können die Zeichenfolge optimieren, wenn Sie sich dafür interessieren, wie sie aussieht).
`pwd`/`dirname $0`
aber kann immer noch auf Symlinks fehlschlagen