Fondamentalement, je dois exécuter le script avec des chemins 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ù se trouve le fichier de script?
Dans Bash, vous devriez obtenir ce dont vous avez besoin comme ceci:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
Le message original 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 l’option -f
. Fonctionne lorsque le script est appelé par un absolu aussi bien 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
Un commentaire précédent sur une réponse l'a dit, mais il est facile de rater toutes les autres réponses.
Lorsque vous utilisez bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
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 il se trouve. 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 à partir du résultat d'une commande, enveloppez-la dans $(
et )
- ou vous n'obtiendrez pas le résultat souhaité.
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}"
Comme le suggère le Marko:
BASEDIR=$(dirname $0)
echo $BASEDIR
Cela ne fonctionne que si vous exécutez le script à partir du même répertoire que celui-ci, auquel cas vous obtenez la valeur '.'
Pour résoudre ce problème, utilisez:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
Vous pouvez maintenant utiliser la variable current_dir tout au long de votre script pour faire référence au répertoire du script. Cependant, cela peut toujours avoir le problème du lien symbolique.
La meilleure réponse à cette question a été répondue ici:
Obtenir le répertoire source d'un script Bash de l'intérieur
Et 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'"
cd $(dirname $(readlink -f $0))
Faisons-en un oneliner POSIX:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a"; pwd)
Testé sur de nombreux coques compatibles Bourne, y compris ceux BSD.
Autant que je sache, je suis l'auteur et je le mets dans le domaine public. Pour plus d'informations, voir: https://www.jasan.tk/posts/2017-05-11-posix_Shell_dirname_replacement/
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
INTRODUCTION
Cette réponse corrige la réponse très brisée mais choquante du vote supérieur de ce fil (écrit par TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
POURQUOI UTILISER 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"
$ représente la première partie de la commande appelant le script (il s’agit essentiellement de la commande entrée sans les arguments:
/some/path/./script argument1 argument2
$ 0 = "/ some/path /./ script"
dirname trouve fondamentalement le dernier/dans une chaîne et le tronque à cet endroit. 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
Dites 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 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 dans lesquelles basedir $ 0 fonctionnera si l'utilisateur utilise sh ou touch pour lancer un script, car les deux résultent en $ 0:
$0=/some/path/script
ce qui vous donnera un chemin que vous pouvez utiliser avec dirname.
LA SOLUTION
Vous auriez un compte et détecteriez chacune des situations mentionnées ci-dessus et appliqueriez une solution si elle se présentait:
#!/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
Si vous voulez obtenir le répertoire de script réel (que vous appeliez le script via un lien symbolique ou directement), essayez:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
Cela fonctionne à la fois sous linux et macOS. Je ne voyais personne ici mentionner realpath
. Je ne sais pas s'il y a des inconvénients à cette approche.
sur macOS, vous devez installer coreutils
pour utiliser realpath
. Par exemple: brew install coreutils
.
Ce one-liner indique où se trouve le script Shell, peu importe si vous l'avez exécuté ou s'il vous a été attribué. 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"))
En passant, je suppose que vous utilisez / bin/bash.
Autant de réponses, toutes plausibles, chacune avec des objectifs favorables et défavorables (qui devraient probablement être énoncés pour chacune). Voici une autre solution qui répond à un objectif primordial: être clair et fonctionner sur tous les systèmes, sur tous les systèmes bash (pas d’hypothèses sur les versions bash, ni sur les options readlink
ou pwd
), et fait raisonnablement ce que vous feriez. Attendez-vous à ce que cela se produise (par exemple, la résolution des liens symboliques est un problème intéressant, mais qui n’est pas ce que vous voulez réellement), gérez les cas Edge comme les espaces dans les chemins, etc., ignore les erreurs et utilise une valeur par défaut si nécessaire.
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