Comment obtenir le chemin du répertoire dans lequel se trouve un script Bash , à l' intérieur de ce script?
Je veux utiliser un script Bash comme lanceur pour une autre application. Je veux changer le répertoire de travail en celui où se trouve le script Bash, donc je peux opérer sur les fichiers dans ce répertoire, comme ceci:
$ ./application
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- et le supprimer sans substitution de commande -DIR="${DIR%x}"
.mkdir $'\n'
.dirname
et que le répertoire pourrait commencer par un-
(par exemple--help
).DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}
. C'est peut-être exagéré?Réponses:
est une ligne utile qui vous donnera le nom de répertoire complet du script, peu importe d'où il est appelé.
Cela fonctionnera tant que le dernier composant du chemin utilisé pour trouver le script n'est pas un lien symbolique (les liens de répertoire sont OK). Si vous souhaitez également résoudre les liens vers le script lui-même, vous avez besoin d'une solution multi-lignes:
Ce dernier travaillera avec toute combinaison d'alias,
source
,bash -c
, liens symboliques, etc.Attention: si vous accédez
cd
à un répertoire différent avant d'exécuter cet extrait, le résultat peut être incorrect!Faites également attention aux
$CDPATH
gotchas et aux effets secondaires de sortie de stderr si l'utilisateur a outrepassé intelligemment le cd pour rediriger la sortie vers stderr à la place (y compris les séquences d'échappement, comme lors d'un appelupdate_terminal_cwd >&2
sur Mac). L'ajout>/dev/null 2>&1
à la fin de votrecd
commande prendra en charge les deux possibilités.Pour comprendre comment cela fonctionne, essayez d'exécuter ce formulaire plus détaillé:
Et il imprimera quelque chose comme:
la source
source <script>
etbash <script>
:DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
.cd
quelque chose sur STDOUT! Par exemple, si votre$CDPATH
a.
. Pour couvrir ce cas, utilisezDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
dirname $(readlink -f $0)
est la bonne commande. Voir gist.github.com/tvlooy/cbfbdb111a4ebad8b93e pour un testcasedirname "$(readlink -f "$0")"
n'ajoute pas de complexité et est une mesure juste plus robuste pour un minimum de problèmes.readlink -f $0
donnereadlink: illegal option -- f
.Utilisation
dirname "$0"
:l'utilisation
pwd
seule ne fonctionnera pas si vous n'exécutez pas le script à partir du répertoire dans lequel il se trouve.la source
type -p
si le script est exécutable. Cela peut également ouvrir un trou subtil si le script est exécuté à l'aidebash test2.sh
et qu'il existe un autre script avec le même nom exécutable ailleurs.bash
et que la ligne de hachage-bang mentionne explicitement,/bin/bash
je dirais qu'il est assez sûr de dépendre des bashismes.dirname $0
est que si le répertoire est le répertoire courant, vous obtiendrez.
. C'est bien, sauf si vous allez changer de répertoire dans le script et vous attendre à utiliser le chemin que vous avez obtenudirname $0
comme s'il était absolu. Pour obtenir le chemin absolu:pushd `dirname $0` > /dev/null
,SCRIPTPATH=`pwd`
,popd > /dev/null
: pastie.org/1489386 (Mais sûrement il y a une meilleure façon d'élargir cette voie?)dirname $0
soit un problème si vous l'assignez à une variable et l'utilisez ensuite pour lancer un script comme$dir/script.sh
; J'imagine que c'est le cas d'utilisation pour ce type de chose dans 90% des cas../script.sh
fonctionnerait bien.La
dirname
commande est la plus simple, en analysant simplement le chemin jusqu'au nom de fichier de la$0
variable (nom du script):Mais, comme l'a souligné matt b , le chemin renvoyé est différent selon la façon dont le script est appelé.
pwd
ne fait pas le travail car cela vous indique uniquement le répertoire actuel, pas le répertoire dans lequel se trouve le script. De plus, si un lien symbolique vers un script est exécuté, vous obtiendrez un chemin (probablement relatif) vers où réside le lien, pas le script réel.Certains autres ont mentionné la
readlink
commande, mais dans sa plus simple expression, vous pouvez utiliser:readlink
résoudra le chemin du script en chemin absolu depuis la racine du système de fichiers. Ainsi, tous les chemins contenant des points simples ou doubles, des tildes et / ou des liens symboliques seront résolus en un chemin complet.Voici un script illustrant chacun de ces éléments
whatdir.sh
:Exécuter ce script dans mon répertoire personnel, en utilisant un chemin relatif:
Encore une fois, mais en utilisant le chemin d'accès complet au script:
Modification des répertoires:
Et enfin en utilisant un lien symbolique pour exécuter le script:
la source
readlink
ne sera pas disponible sur certaines plates-formes dans l'installation par défaut. Essayez d'éviter de l'utiliser si vous le pouvezexport SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
-f
n'est pas reconnu comme une option pourreadlink
. Utiliser à lastat -f
place fait le travail. Mercigreadlink
, qui est fondamentalement le quereadlink
nous connaissons tous. Voici une version indépendante de la plateforme:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
greadlink
peut facilement être installé via homebrew:brew install coreutils
Fonctionne pour toutes les versions, y compris
source
" aka.
(point).$0
est modifié depuis l'appelant."./script"
"/full/path/to/script"
"/some/path/../../another/path/script"
"./some/folder/script"
Alternativement, si le script bash lui-même est un lien symbolique relatif, vous voulez le suivre et renvoyer le chemin complet du script lié:
SCRIPT_PATH
est donné en chemin complet, peu importe comment il est appelé.Assurez-vous simplement de localiser cela au début du script.
Ce commentaire et code Copyleft, licence sélectionnable sous GPL2.0 ou ultérieure ou CC-SA 3.0 (CreativeCommons Share Alike) ou ultérieure. (c) 2008. Tous droits réservés. Aucune garantie d'aucune sorte. Tu étais prévenu.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
la source
readlink -f $(dirname "${VIRTUAL_ENV}")
;dirname "${SCRIPT_PATH}"
&& pwd)? Mais de toute façon, un excellent script!cd
sortir de son répertoire actuel dans l'espoir d'ycd
revenir plus tard: le script peut ne pas avoir l'autorisation de revenir au répertoire qui était en cours lors de son appel. (Idem pour pushd / popd)readlink -f
est spécifique à GNU. BSDreadlink
n'a pas cette option.([ ... ])
est moins efficace que[ ... ]
, et il n'y a aucun avantage tiré de l'isolement offert en échange de cette performance frappée ici.Réponse courte:
ou (de préférence ):
la source
source
etcd $(dirname $0)
c'est facile à retenir.${BASH_SOURCE[0]}
au lieu de$0
fonctionnera avecsource my/script.sh
${BASH_SOURCE[0]}
n'est pas satisfaisant du tout.${BASH_SOURCE:-0}
c'est beaucoup mieux.Vous pouvez utiliser
$BASH_SOURCE
:Notez que vous devez utiliser
#!/bin/bash
et non#!/bin/sh
car c'est une extension Bash.la source
./foo/script
, alors$(dirname $BASH_SOURCE)
c'est./foo
.realpath
commande pour obtenir le chemin complet de ./foo/script. Ainsidirname $(realpath ./foo/script)
donnera le chemin du script.Cela devrait le faire:
Cela fonctionne avec les liens symboliques et les espaces dans le chemin.
Voir les pages de manuel pour
dirname
etreadlink
.D'après la piste des commentaires, il ne semble pas fonctionner avec Mac OS. Je n'ai pas d'idées pourquoi c'est comme ça. Aucune suggestion?
la source
./script.sh
shows.
au lieu du chemin de répertoire completstat
plutôt. Mais quand même, cela montre.
si vous êtes dans «ce» répertoire.coreutils
partir de Homebrew et utilisergreadlink
pour obtenir l'-f
option sur MacOS car c'est * BSD sous les couvertures et non Linux.DIR="$(dirname "$(readlink -f "$0")")"
pwd
peut être utilisé pour trouver le répertoire de travail actuel etdirname
pour trouver le répertoire d'un fichier particulier (la commande qui a été exécutée est$0
,dirname $0
devrait donc vous donner le répertoire du script actuel).Cependant,
dirname
donne précisément la partie répertoire du nom de fichier, qui sera plus que probablement relative au répertoire de travail actuel. Si votre script doit changer de répertoire pour une raison quelconque, la sortie dedirname
devient vide de sens.Je suggère ce qui suit:
De cette façon, vous obtenez un répertoire absolu plutôt que relatif.
Étant donné que le script sera exécuté dans une instance bash distincte, il n'est pas nécessaire de restaurer le répertoire de travail par la suite, mais si vous souhaitez revenir dans votre script pour une raison quelconque, vous pouvez facilement attribuer la valeur de
pwd
à une variable avant de changer de répertoire, pour une utilisation future.Bien que juste
résout le scénario spécifique dans la question, je trouve avoir le chemin absolu vers plus plus utile en général.
la source
dirname $0
&& pwd)Je suis fatigué de revenir sur cette page encore et encore pour copier-coller le one-liner dans la réponse acceptée. Le problème, c'est qu'il n'est pas facile à comprendre et à retenir.
Voici un script facile à retenir:
la source
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
realpath
entre la résolution «manuelle» et la boucle dereadlink
? Même lareadlink
page de manuel ditNote realpath(1) is the preferred command to use for canonicalization functionality.
realpath
avantdirname
, pas après? Si le fichier script lui-même est un lien symbolique ... Cela donnerait quelque chose commeDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. En fait très proche de la réponse proposée par Simon.Je ne pense pas que ce soit aussi facile que d'autres l'ont fait croire.
pwd
ne fonctionne pas, car le répertoire courant n'est pas nécessairement le répertoire avec le script.$0
n'a pas toujours les informations non plus. Considérez les trois façons suivantes pour appeler un script:Dans la première et la troisième manière,
$0
les informations de chemin d'accès ne sont pas complètes. Dans les deuxième et troisième,pwd
ne fonctionne pas. La seule façon d'obtenir le répertoire de la troisième manière serait de parcourir le chemin et de trouver le fichier avec la bonne correspondance. Fondamentalement, le code devrait refaire ce que fait le système d'exploitation.Une façon de faire ce que vous demandez serait de simplement coder en dur les données dans le
/usr/share
répertoire et de les référencer par son chemin complet. Les données ne doivent de/usr/bin
toute façon pas être dans le répertoire, c'est donc probablement la chose à faire.la source
la source
$0
ni nepwd
sont garantis d'avoir les bonnes informations, selon la façon dont le script est appelé.la source
$BASH_SOURCE
plus$0
, car c'est explicite même pour les lecteurs qui ne connaissent pas bien bash.$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Cela obtient le répertoire de travail actuel sur Mac OS X 10.6.6:
la source
Ceci est spécifique à Linux, mais vous pouvez utiliser:
la source
/proc/fd/$$/255
semble pointer vers le tty, pas vers un répertoire. Par exemple, dans mon shell de connexion actuel, les descripteurs de fichiers 0, 1, 2 et 255 font tous référence à/dev/pts/4
. Dans tous les cas, le manuel bash ne mentionne pas le fd 255, il est donc peu judicieux de dépendre de ce comportement. \realpath ${BASH_SOURCE[0]};
qu'il en soit, cela semble être la meilleure façon de procéder.Voici une doublure conforme POSIX:
la source
cd
est configuré pour imprimer le nouveau nom de chemin.J'ai essayé tout cela et aucun n'a fonctionné. L'un était très proche mais avait un minuscule bug qui le cassait gravement; ils ont oublié de mettre le chemin entre guillemets.
De plus, beaucoup de gens supposent que vous exécutez le script à partir d'un shell, alors ils oublient que lorsque vous ouvrez un nouveau script, il revient par défaut à votre domicile.
Essayez ce répertoire sur pour la taille:
Cela vous convient, peu importe comment ou où vous l'exécutez:
Donc, pour le rendre réellement utile, voici comment passer au répertoire du script en cours d'exécution:
la source
ln -s ../bin64/foo /usr/bin/foo
).Voici la manière simple et correcte:
Explication:
${BASH_SOURCE[0]}
- le chemin complet vers le script. La valeur de ceci sera correcte même lorsque le script est généré, par exemple,source <(echo 'echo $0')
imprime bash , tandis que le remplacer par${BASH_SOURCE[0]}
affichera le chemin complet du script. (Bien sûr, cela suppose que vous êtes OK en prenant une dépendance à Bash.)readlink -f
- Résout récursivement tous les liens symboliques dans le chemin spécifié. Il s'agit d'une extension GNU et non disponible sur (par exemple) les systèmes BSD. Si vous utilisez un Mac, vous pouvez utiliser Homebrew pour installer GNUcoreutils
et le remplacer pargreadlink -f
.Et bien sûr,
dirname
obtient le répertoire parent du chemin.la source
greadlink -f
malheureusement, ne fonctionne pas efficacement lors desource
l'La façon la plus courte et la plus élégante de le faire est:
Cela fonctionnerait sur toutes les plateformes et est super propre.
Plus de détails peuvent être trouvés dans "Dans quel répertoire se trouve ce script bash? ".
la source
J'utiliserais quelque chose comme ça:
la source
sh
aussi! Problème avec desdirname "$0"
solutions simples : si le script se trouve dans le$PATH
et est invoqué sans chemin, ils donneront un résultat incorrect.PATH
,$0
contiendra le nom de fichier absolu. Si le script est invoqué avec un nom de fichier relatif ou absolu contenant un/
,$0
le contiendra.Il s'agit d'une légère révision de la solution e-satis et 3bcdnlklvc04a soulignée dans leur réponse :
Cela devrait toujours fonctionner dans tous les cas énumérés.
Cela évitera
popd
après un échecpushd
, grâce à konsolebox.la source
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
popd
dans les cas (même rares) oùpushd
échoue. Et en cas d'pushd
échec, quelle devrait être, selon vous, la valeurSCRIPT_DIR
? L'action peut varier en fonction de ce qui peut sembler logique ou de ce qu'un utilisateur pourrait préférer, mais il est certain que fairepopd
est mal.pushd
popd
dangers pourraient être évités simplement en les supprimant et en utilisant à la placecd
+pwd
enfermé dans une substitution de commande.SCRIPT_DIR=$(...)
Pour les systèmes ayant des coreutils GNU
readlink
(par ex. Linux):Il n'est pas nécessaire d'utiliser
BASH_SOURCE
lorsque$0
contient le nom de fichier du script.la source
dirname
appel. Nécessaire si le chemin du répertoire contient des espaces.la source
$0
ne fonctionnera pas pour un script d'origine$_
mérite d'être mentionné comme une alternative à$0
. Si vous exécutez un script à partir de Bash, la réponse acceptée peut être raccourcie en:Notez que cela doit être la première instruction de votre script.
la source
source
ou.
le script. Dans ces situations,$_
contiendrait le dernier paramètre de la dernière commande que vous avez exécutée avant le.
.$BASH_SOURCE
fonctionne à chaque fois.J'ai comparé bon nombre des réponses données et j'ai trouvé des solutions plus compactes. Ceux-ci semblent gérer tous les cas de bord fou qui découlent de votre combinaison préférée de:
script
,bash script
,bash -c script
,source script
ou. script
Si vous exécutez sous Linux, il semble que l'utilisation du
proc
handle soit la meilleure solution pour localiser la source entièrement résolue du script en cours d'exécution (dans une session interactive, le lien pointe vers le respectif/dev/pts/X
):Cela a un peu de laideur, mais le correctif est compact et facile à comprendre. Nous n'utilisons pas uniquement les primitives bash, mais je suis d'accord avec cela car cela
readlink
simplifie considérablement la tâche. Leecho X
ajoute unX
à la fin de la chaîne de variable afin que les espaces de fin du nom de fichier ne soient pas consommés et que la substitution de paramètre${VAR%X}
à la fin de la ligne supprime leX
. Parce qu'ilreadlink
ajoute une nouvelle ligne qui lui est propre (qui serait normalement consommée dans la substitution de commandes si ce n'était pour notre supercherie précédente), nous devons également nous en débarrasser. Ceci est plus facilement accompli en utilisant le$''
schéma de citation, qui nous permet d'utiliser des séquences d'échappement telles que\n
pour représenter les retours à la ligne (c'est aussi ainsi que vous pouvez facilement créer des répertoires et des fichiers nommés de manière détournée).Ce qui précède devrait couvrir vos besoins pour localiser le script en cours d'exécution sur Linux, mais si vous n'avez pas le
proc
système de fichiers à votre disposition, ou si vous essayez de localiser le chemin entièrement résolu d'un autre fichier, alors peut-être que vous trouver le code ci-dessous utile. Ce n'est qu'une légère modification par rapport à la doublure ci-dessus. Si vous jouez autour avec répertoire / étranges noms de fichiers, en vérifiant la sortie à la foisls
etreadlink
est informative, commels
Affichera chemins « simplifiée », en substituant?
pour des choses comme les nouvelles lignes.la source
/dev/pts/30
avec bash sur Ubuntu 14.10 Desktop.echo $resolved
, je sauvé commed
,chmod +x d
,./d
.#!/bin/bash
Essayez d'utiliser:
la source
$0
pour${BASH_SOURCE[0]}
que cette méthode fonctionne n'importe où, y compris dans une fonction.dirname
car la dernière partie de$0
peut être un lien symbolique pointant vers un fichier qui ne se trouve pas dans le même répertoire que le lien symbolique lui-même. La solution décrite dans cette réponse obtient simplement le chemin du répertoire où le lien symbolique qu'il a stocké, pas le répertoire de la cible. De plus, cette solution manque de citation. Cela ne fonctionnera pas si le chemin contient des caractères spéciaux.dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Essayez la solution de compatibilité croisée suivante:
car les commandes telles que
realpath
oureadlink
pourraient ne pas être disponibles (selon le système d'exploitation).Remarque: Dans Bash, il est recommandé d'utiliser à la
${BASH_SOURCE[0]}
place de$0
, sinon le chemin peut se casser lors de la recherche du fichier (source
/.
).Vous pouvez également essayer la fonction suivante dans bash:
Cette fonction prend 1 argument. Si l'argument a déjà un chemin absolu, imprimez-le tel quel, sinon imprimez l'
$PWD
argument variable + nom de fichier (sans./
préfixe).En relation:
la source
realpath
fonction @Chris prend 1 argument. Si l'argument a déjà un chemin absolu, imprimez-le tel quel, sinon imprimez$PWD
+ nom de fichier (sans./
préfixe).Je crois que j'ai celui-ci. Je suis en retard à la fête, mais je pense que certains apprécieront d'être ici s'ils rencontrent ce fil. Les commentaires doivent expliquer:
la source
Ce sont de courtes façons d'obtenir des informations sur le script:
Dossiers et fichiers:
En utilisant ces commandes:
Et j'ai obtenu cette sortie:
Voir également: https://pastebin.com/J8KjxrPF
la source
Cela fonctionne dans bash-3.2:
Si vous avez un
~/bin
répertoire dans votre$PATH
, vous en avezA
dans ce répertoire. Il source le script~/bin/lib/B
. Vous savez où le script inclus est relatif à celui d'origine, dans lelib
sous - répertoire, mais pas où il est relatif au répertoire actuel de l'utilisateur.Ceci est résolu par ce qui suit (à l'intérieur
A
):Peu importe où se trouve l'utilisateur ou comment il appelle le script, cela fonctionnera toujours.
la source
which
est très discutable.type
,hash
Et d' autres fonctions intégrées font la même chose de mieux dans bash.which
est un peu plus portable, bien qu'il ne soit pas vraiment le même que celuiwhich
utilisé dans d'autres shells comme tcsh, qui en fait un intégré.which
étant un outil externe, vous n'avez aucune raison de croire qu'il se comporte de manière identique au shell parent.La meilleure solution compacte à mon avis serait:
Il n'y a aucune dépendance à autre chose que Bash. L'utilisation de
dirname
,readlink
etbasename
conduira éventuellement à des problèmes de compatibilité, il est donc préférable d'éviter autant que possible.la source
"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"
. Vous auriez des problèmes avec le répertoire racine si vous ne le faites pas. Aussi pourquoi devez-vous même utiliser l'écho?dirname
etbasename
sont POSIX standardisés, alors pourquoi éviter de les utiliser? Liens:dirname
,basename