Comment définir le répertoire de travail actuel sur le répertoire du script?

569

J'écris un script bash. J'ai besoin que le répertoire de travail actuel soit toujours le répertoire dans lequel se trouve le script.

Le comportement par défaut est que le répertoire de travail actuel dans le script est celui du shell à partir duquel je l'exécute, mais je ne souhaite pas ce comportement.

jameshfisher
la source
Avez-vous envisagé de placer un script wrapper quelque part comme / usr / bin dans le répertoire approprié (codé en dur) puis d'exécuter votre script?
Dagg Nabbit
2
Pourquoi avez-vous besoin du répertoire du script? Il existe probablement une meilleure façon de résoudre le problème sous-jacent.
bstpierre
12
Je voudrais juste souligner que le comportement que vous appelez "évidemment indésirable" est en fait tout à fait nécessaire - si je lance, myscript path/to/fileje m'attends à ce que le script évalue le chemin / vers / fichier par rapport à mon répertoire actuel, pas quel que soit le répertoire dans lequel le script se produit être situé dans. Aussi, que serait-il arrivé pour un script exécuté avec ssh remotehost bash < ./myscriptcomme le mentionne la FAQ BASH?
Gordon Davisson
3
cd "${BASH_SOURCE%/*}" || exit
caw

Réponses:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
la source
7
dirname renvoie '.' lors de l'utilisation de bash sous Windows. Donc, la réponse de Paul est meilleure.
Tvaroh
7
Renvoie également '.' dans Mac OSX
Ben Clayton
4
Il convient de noter que les choses peuvent se briser si un lien symbolique en fait partie $0. Dans votre script, vous pouvez vous attendre, par exemple, ../../à faire référence au répertoire deux niveaux au-dessus de l'emplacement du script, mais ce n'est pas nécessairement le cas si des liens symboliques sont en jeu.
Brian Gordon
20
Si vous avez appelé le script en tant que ./script, .est le bon répertoire, et le changer se .terminera également dans le répertoire même où il scriptse trouve, c'est-à-dire dans le répertoire de travail actuel.
ndim
6
Si vous exécutez le script à partir du répertoire actuel de cette manière bash script.sh, la valeur de $0est script.sh. La seule façon dont la cdcommande "fonctionnera" pour vous est que vous ne vous souciez pas des commandes ayant échoué. Si vous deviez utiliser set -o errexit(aka:) set -epour vous assurer que votre script ne dépasse pas les commandes ayant échoué, cela ne fonctionnerait PAS car il cd script.shs'agit d'une erreur. La méthode fiable [spécifique à bash] estcd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky
322

Ce qui suit fonctionne également:

cd "${0%/*}"

La syntaxe est décrite en détail dans cette réponse StackOverflow.

Paul Schulz
la source
17
Explication comment cela fonctionne: stackoverflow.com/questions/6393551/…
kenorb
25
N'oubliez pas de mettre entre guillemets si le chemin contient des espaces. ie cd "$ {0% / *}"
H.Rabiee
17
Cela échoue si le script est appelé avec bash script.sh- $0sera juste le nom du fichier
Chris Watts
17
Je dirais que cette réponse est trop succincte. On n'apprend presque rien sur la syntaxe. Une description de la façon de lire ceci serait utile.
fraxture
2
Cette astuce ne fait pas apparaître un sous-shell aussi, donc c'est bon pour les monstres de vitesse.
teknopaul
184

Essayez les simples lignes simples suivantes:


Pour tous UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Frapper

dir=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

Remarque: Un double tiret (-) est utilisé dans les commandes pour signifier la fin des options de commande, afin que les fichiers contenant des tirets ou d'autres caractères spéciaux ne cassent pas la commande.

Remarque: Dans Bash, utilisez ${BASH_SOURCE[0]}en faveur de $0, sinon le chemin peut se casser lors de son sourcing ( source/ .).


Pour Linux, Mac et autres * BSD:

cd "$(dirname "$(realpath "$0")")";

Remarque: realpathdevrait être installé par défaut dans la distribution Linux la plus populaire (comme Ubuntu), mais dans certains cas, il peut être manquant, vous devez donc l'installer.

Remarque: Si vous utilisez Bash, utilisez ${BASH_SOURCE[0]}en faveur de $0, sinon le chemin peut se casser lors de son sourcing ( source/. ).

Sinon, vous pouvez essayer quelque chose comme ça (il utilisera le premier outil existant):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Pour Linux spécifique:

cd "$(dirname "$(readlink -f "$0")")"

Utilisation de GNU readlink sur * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Remarque: Vous devez avoir coreutilsinstallé (par exemple 1. Installez Homebrew , 2. brew install coreutils).


En bash

Dans bash, vous pouvez utiliser des extensions de paramètres pour y parvenir, comme:

cd "${0%/*}"

mais cela ne fonctionne pas si le script est exécuté à partir du même répertoire.

Vous pouvez également définir la fonction suivante dans bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Cette fonction prend 1 argument. Si l'argument a déjà un chemin absolu, imprimez-le tel quel, sinon imprimez l' $PWDargument variable + nom de fichier (sans ./préfixe).

ou voici la version tirée du .bashrcfichier Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

En relation:

Voir également:

Comment puis-je obtenir le comportement de readlink -f de GNU sur un Mac?

kenorb
la source
4
une bien meilleure réponse que les plus populaires car elle résout les liens système et rend compte de différents systèmes d'exploitation. Merci!
Dennis
Merci pour cette réponse. Celui du haut fonctionnait sur mon Mac ... mais que fait le commutateur - dans la cpcommande, @kenorb?
TobyG
3
Un double tiret ( --) est utilisé dans les commandes pour signifier la fin des options de commande, afin que les fichiers contenant des tirets ou d'autres caractères spéciaux ne cassent pas la commande. Essayez par exemple de créer le fichier via touch "-test"et touch -- -test, puis supprimez le fichier via rm "-test"et rm -- -test, et voyez la différence.
kenorb
1
Je supprimerais mon vote positif si je le pouvais: realpath est déprécié unix.stackexchange.com/questions/136494/…
lsh
1
@lsh Il est dit que seul le realpathpaquet Debian est obsolète, pas GNU realpath. Si vous pensez que ce n'est pas clair, vous pouvez suggérer une modification.
kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

C'est facile. Ça marche.

Dan Moulding
la source
1
Ce devrait être la réponse acceptée. Fonctionne sur OSX et Linux Ubuntu.
David Rissato Cruz
5
Cela fonctionne très bien lorsque le script B provient d'un autre script A et que vous devez utiliser des chemins relatifs au script B. Merci.
Enrico
5
Cela fonctionne également dans Cygwin pour ceux d'entre nous qui ont la malchance d'avoir à toucher Windows.
Bruno Bronosky
3
Oucd "${BASH_SOURCE%/*}" || exit
caw
Fonctionne sur macOS Mojave
Juan Boero
6

La réponse acceptée fonctionne bien pour les scripts qui n'ont pas de lien symbolique ailleurs, comme dans $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Cependant, si le script est exécuté via un lien symbolique,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Ce sera cd dans le ~/bin/répertoire et non le ~/project/répertoire, ce qui cassera probablement votre script si le but de lacd est d'inclure des dépendances par rapport à~/project/

La réponse sûre de lien symbolique est ci-dessous:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f est requis pour résoudre le chemin absolu du fichier potentiellement lié.

Les guillemets sont nécessaires pour prendre en charge les chemins de fichiers qui pourraient potentiellement contenir des espaces blancs (mauvaise pratique, mais il n'est pas sûr de supposer que ce ne sera pas le cas)

James McGuigan
la source
4

Ce script semble fonctionner pour moi:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

La ligne de commande pwd fait écho à l'emplacement du script en tant que répertoire de travail actuel, peu importe d'où je l'exécute.

Amardeep AC9MF
la source
3
realpathest peu susceptible d'être installé partout. Ce qui peut ne pas avoir d'importance, selon la situation du PO.
bstpierre
Mon système n'en a pas realpathmais il en a readlinkqui semble être similaire.
pause jusqu'à nouvel ordre.
2
Quelle dist n'a pas realpath installé par défaut? Ubuntu l'a.
kenorb
1
cd "`dirname $(readlink -f ${0})`"

la source
Pouvez-vous expliquer votre réponse s'il vous plaît?
Zulu
1
Bien que ce code puisse aider à résoudre le problème, il n'explique pas pourquoi et / ou comment il répond à la question. Fournir ce contexte supplémentaire améliorerait considérablement sa valeur éducative à long terme. Veuillez modifier votre réponse pour ajouter des explications, y compris les limitations et hypothèses applicables.
Toby Speight
-5
echo $PWD

PWD est une variable d'environnement.

Maverick Holdem
la source
5
Cela donne uniquement le répertoire appelé depuis, pas le répertoire où se trouve le script.
dagelf
-6

Si vous avez juste besoin d'imprimer le répertoire de travail actuel, vous pouvez suivre ceci.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Accordez l'autorisation d'exécution:

chmod u+x test

Exécutez ensuite le script d'ici ./testlà, vous pouvez voir le répertoire de travail actuel.

Chandan
la source
2
La question était de savoir comment s'assurer que le script s'exécute dans son propre répertoire, y compris s'il ne s'agit pas du répertoire de travail. Ce n'est donc pas une réponse. Quoi qu'il en soit, pourquoi faire cela au lieu de simplement ... courir pwd? Il me semble que beaucoup de touches ont été gaspillées.
underscore_d