Accéder au répertoire à l'aide des variables bash ne fonctionne pas lorsque les noms de répertoire ont des espaces

11

Disons que je veux stocker la commande suivante dans une variable

cd "/cygdrive/c/Program Files/"

Donc je fais ça

dir="cd \"/cygdrive/c/Program Files/\""

Cela devrait stocker la commande pour naviguer dans le répertoire Program Files, donc quand je tape $ dir, il m'emmène dans ce répertoire. Pour vérifier que les citations ont été correctement échappées, je tape

echo $dir

ce qui me donne

cd "/cygdrive/c/Program Files/"

Donc, tout devrait bien fonctionner. Cependant, quand je tape,

$dir

Je reçois

bash: cd: "/cygdrive/c/Program: No such file or directory

Qu'est-ce que je fais mal? J'utilise Cygwin, mais je suppose que ce problème s'applique à bash en général.

gsingh2011
la source
Essayez de supprimer les guillemets autour du nom du répertoire et échappez à la place avec une barre oblique inverse.
Mike Fitzpatrick

Réponses:

8

Réponse courte: voir BashFAQ # 050 ("J'essaie de mettre une commande dans une variable, mais les cas complexes échouent toujours!").

Réponse longue: lorsque bash analyse une commande, il analyse les guillemets avant de remplacer les variables; il ne revient jamais en arrière et ré-analyse les guillemets dans les valeurs des variables, donc ils finissent par ne rien faire d'utile. Utiliser echo pour vérifier la commande est complètement trompeur car il affiche la commande après son analyse; si vous voulez voir ce qui est réellement exécuté, utilisez soit set -xles commandes d'impression shell lors de leur exécution, soit utilisez printf "%q " $dir; echoplutôt que l'écho.

Si vous souhaitez stocker une commande complexe (c'est-à-dire avec des espaces ou d'autres caractères spéciaux dans les "mots"), vous devez la placer dans un tableau au lieu d'une simple variable de texte, puis la développer avec l'idiome `" $ { tableau [@]} ", comme ceci:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Maintenant, si le but est de créer un raccourci facile à taper, ce n'est clairement pas la voie à suivre. Utilisez plutôt un alias ou une fonction shell:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

ou

dir() { cd "/cygdrive/c/Program Files/"; }
dir
Gordon Davisson
la source
7

Lorsque bash se développe $dir, il n'effectue que la division et la globalisation des mots. Fractionnement mot produit trois mots: cd, "/cygdrive/c/Programet Files/". Ensuite, la commande à exécuter est cdavec deux arguments; cdne regarde que son premier argument "/cygdrive/c/Program, qui n'est pas un répertoire existant.

Si vous souhaitez effectuer une évaluation complète du shell sur le contenu d'une variable, utilisez eval:

eval "$dir"

Notez que vous avez besoin des guillemets doubles $dir, sinon le fractionnement de mots serait d'abord effectué, puis evalconcaténerait ses arguments avec des espaces. Cela se produirait ici, mais irait mal en général (par exemple, s'il y avait deux espaces consécutifs dans un nom de fichier).

Cependant, une chaîne n'est pas le bon moyen de stocker une commande shell que vous souhaitez exécuter. Sauf si vous avez une exigence inhabituelle, vous devez plutôt utiliser une fonction:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Si vous êtes vraiment intéressé à taper $dirpour basculer vers un répertoire particulier et ce n'est pas seulement un exemple simple, depuis bash 4, mettez shopt -s autocdvotre .bashrcet définissez

dir="/cygdrive/c/Program Files/"

après quoi vous pouvez taper juste "/cygdrive/c/Program Files/"ou "$dir"à l'invite du shell pour basculer vers ce répertoire. Vous avez toujours besoin des guillemets $dir; si vous n'aimez pas cela, utilisez zsh au lieu de bash.

Gilles 'SO- arrête d'être méchant'
la source
6

Stocker des commandes dans des variables n'est généralement pas une bonne idée. C'est à cela que servent les fonctions et les alias.

Plutôt que

dir="cd \"/cygdrive/c/Program Files/\""

essayez l'un d'eux:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

ou

dir() { cd "/cygdrive/c/Program Files"; }
dir

ou

alias dir='cd "/tmp/Program Files"'
...
dir

Le premier formulaire, stockant le nom du répertoire dans une variable, signifie un peu plus de frappe, mais c'est plus clair lorsque vous exécutez la commande que vous faites cd. Le second est le plus proche de ce que vous essayez d'accomplir. (Je n'utilise pas beaucoup d'alias en bash; je ne sais pas vraiment quel avantage ils ont sur les fonctions.)

ÉDITER :

Et dirc'est probablement un mauvais nom pour un alias ou une fonction, car il existe une commande existante sous ce nom (c'est essentiellement une version de ls, du moins si vous avez des coreutils GNU).

Keith Thompson
la source
vote positif pourStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob