Fondamentalement, j'ai besoin d'exécuter le script avec des chemins d'accès liés à l'emplacement du fichier de script shell, comment puis-je changer le répertoire actuel dans le même répertoire que celui où réside le fichier de script?
Fondamentalement, j'ai besoin d'exécuter le script avec des chemins d'accès liés à l'emplacement du fichier de script shell, comment puis-je changer le répertoire actuel dans le même répertoire que celui où réside le fichier de script?
Réponses:
Dans Bash, vous devriez obtenir ce dont vous avez besoin comme ceci:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
readlink
(voir la réponse de al ci-dessous)
$BASH_SOURCE
à la place de $0
, car $0
il ne contient pas toujours le chemin du script invoqué, comme lors de la «sourcing» d'un script.
$BASH_SOURCE
est spécifique à Bash, la question concerne le script shell en général.
CUR_PATH=$(pwd)
ou pwd
renvoyez le répertoire courant (qui ne doit pas nécessairement être le répertoire parent des scripts)!
$BASH_SOURCE
, et elle renvoie ce dont j'avais besoin. Mon script est appelé à partir d'un autre script et $0
renvoie .
alors que $BASH_SOURCE
renvoie le sous-répertoire de droite (dans mon cas scripts
).
Le message d'origine contient la solution (ignorez les réponses, elles n'ajoutent rien d'utile). Le travail intéressant est effectué par la commande unix mentionnée readlink
avec option -f
. Fonctionne lorsque le script est appelé par un chemin absolu ainsi que par un chemin relatif.
Pour 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
Pour 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
Voir aussi: https://stackoverflow.com/a/246128/59087
readlink
. C'est pourquoi j'ai recommandé d'utiliser pushd / popd (intégrés pour bash).
-f
option de faire readlink
quelque chose de différent sur OS X (Lion) et éventuellement BSD. stackoverflow.com/questions/1055671/…
-f
n'est pas du tout pris en charge sur OS X (comme pour Lion); là, vous pouvez soit supprimer le -f
pour faire avec la résolution d'au plus un niveau d'indirection, par exemple pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"
, ou vous pouvez rouler votre propre script de suivi de lien symbolique récursif comme illustré dans la publication liée.
sh /some/other/directory/script.sh)
, dans ce cas, ce .
serait votre pwd, pas/some/other/directory
Un commentaire antérieur sur une réponse le disait, mais il est facile de manquer parmi toutes les autres réponses.
Lors de l'utilisation de bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
dirname "$BASH_SOURCE"
place pour gérer les espaces dans $ BASH_SOURCE.
"$(dirname "${BASH_SOURCE[0]}")"
En supposant que vous utilisez bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname $0)
echo $current_dir
echo $script_dir
Ce script doit imprimer le répertoire dans lequel vous vous trouvez, puis le répertoire dans lequel se trouve le script. Par exemple, lorsque vous l'appelez depuis /
avec le script dans /home/mez/
, il génère
/
/home/mez
N'oubliez pas que lorsque vous affectez des variables à la sortie d'une commande, encapsulez la commande dans $(
et )
- ou vous n'obtiendrez pas la sortie souhaitée.
Si vous utilisez bash ....
#!/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
par cd $(dirname "${0}")
et cd -
le faire fonctionner sur d'autres shells, s'ils ont un pwd -L
.
Comme le suggère le Marko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Cela fonctionne à moins que vous n'exécutiez le script à partir du même répertoire où le script réside, auquel cas vous obtenez une valeur de '.'
Pour contourner ce problème, utilisez:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Vous pouvez désormais utiliser la variable current_dir tout au long de votre script pour faire référence au répertoire de script. Cependant, cela peut toujours avoir le problème du lien symbolique.
La meilleure réponse à cette question a été trouvée ici:
Obtenir le répertoire source d'un script Bash de l'intérieur
Et c'est:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner qui vous donnera le nom de répertoire complet du script, peu importe d'où il est appelé.
Pour comprendre comment cela fonctionne, vous pouvez exécuter le script suivant:
#!/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'"
Faisons-en un oneliner POSIX:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Testé sur de nombreux shells compatibles Bourne, y compris ceux BSD.
Pour autant que je sache, je suis l'auteur et je l'ai mis dans le domaine public. Pour plus d'informations, voir: https://www.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
cd: too many arguments
si des espaces dans le chemin d'accès, et retourne $PWD
. (une solution évidente, mais montre juste combien de cas de bord il y a réellement)
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
Si vous souhaitez obtenir le répertoire de script réel (que vous appeliez le script à l'aide d'un lien symbolique ou directement), essayez:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Cela fonctionne à la fois sur linux et macOS. Je ne pouvais voir personne parler ici realpath
. Je ne sais pas s'il y a des inconvénients dans cette approche.
sur macOS, vous devez installer coreutils
pour utiliser realpath
. Par exemple: brew install coreutils
.
INTRODUCTION
Cette réponse corrige la réponse très brisée mais choquante de ce fil (écrite par TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
POURQUOI L'UTILISATION de dirname "$ 0" SUR SON PROPRE NE FONCTIONNE PAS?
dirname $ 0 ne fonctionnera que si l'utilisateur lance le script d'une manière très spécifique. J'ai pu trouver plusieurs situations où cette réponse échoue et bloque le script.
Tout d'abord, comprenons comment cette réponse fonctionne. Il obtient le répertoire de script en faisant
dirname "$0"
$ 0 représente la première partie de la commande appelant le script (c'est essentiellement la commande entrée sans les arguments:
/some/path/./script argument1 argument2
$ 0 = "/ certains / chemin /./ script"
dirname trouve essentiellement le dernier / dans une chaîne et le tronque là. Donc si vous le faites:
dirname /usr/bin/sha256sum
vous obtiendrez: / usr / bin
Cet exemple fonctionne bien car / usr / bin / sha256sum est un chemin correctement formaté mais
dirname "/some/path/./script"
ne fonctionnerait pas bien et vous donnerait:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Disons que vous êtes dans le même répertoire que votre script et que vous le lancez avec cette commande
./script
0 $ dans cette situation sera ./script et dirname $ 0 donnera:
. #or BASEDIR=".", again this will crash your script
En utilisant:
sh script
Sans entrer le chemin complet donnera également un BASEDIR = "."
Utilisation de répertoires relatifs:
../some/path/./script
Donne un nom de répertoire $ 0 de:
../some/path/.
Si vous êtes dans le répertoire / some et que vous appelez le script de cette manière (notez l'absence de / au début, encore une fois un chemin relatif):
path/./script.sh
Vous obtiendrez cette valeur pour dirname $ 0:
path/.
et ./path/./script (une autre forme du chemin relatif) donne:
./path/.
Les deux seules situations où basedir $ 0 fonctionnera sont si l'utilisateur utilise sh ou touch pour lancer un script car les deux donneront $ 0:
$0=/some/path/script
qui vous donnera un chemin que vous pouvez utiliser avec dirname.
LA SOLUTION
Vous devez prendre en compte et détecter chacune des situations mentionnées ci-dessus et appliquer un correctif si cela se produit:
#!/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
Ce one-liner indique où se trouve le script shell, peu importe si vous l'avez exécuté ou si vous l'avez obtenu . En outre, il résout tous les liens symboliques impliqués, si tel est le cas:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
Au fait, je suppose que vous utilisez / bin / bash .
Autant de réponses, toutes plausibles, chacune avec des avantages et des inconvénients et des objectifs légèrement différents (qui devraient probablement être énoncés pour chacun). Voici une autre solution qui répond à un objectif principal à la fois d'être clair et de fonctionner sur tous les systèmes, sur tous les bash (pas d'hypothèses sur les versions de bash readlink
ou les pwd
options), et fait raisonnablement ce que vous attendez (par exemple, la résolution de liens symboliques est un problème intéressant, mais ce n'est généralement pas ce que vous voulez réellement), gérez les cas marginaux comme les espaces dans les chemins, etc., ignore les erreurs et utilise une valeur par défaut saine s'il y a des problèmes.
Chaque composant est stocké dans une variable distincte que vous pouvez utiliser individuellement:
# 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)"
Inspiré par la réponse de Blueyed
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
Cela devrait faire l'affaire:
echo `pwd`/`dirname $0`
Cela peut sembler laid selon la façon dont il a été invoqué et le cwd, mais devrait vous amener là où vous devez aller (ou vous pouvez modifier la chaîne si vous vous souciez de son apparence).
`pwd`/`dirname $0`
mais peut toujours échouer sur les liens symboliques