realpath
et readlink
retourner des chemins absolus:
+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome
Un chemin comme celui-là est facile à gérer. Cependant, quelque chose comme ça va avoir des problèmes:
Par exemple:
Un nom comme celui-ci doit donc être purifié afin de pouvoir le nourrir avec d'autres commandes. Un cas d'utilisation pourrait ressembler à ceci:
+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon
Inutile de dire que vim $bacon
cela ne fonctionnera pas comme prévu.
Que puis-je faire pour assainir ce chemin absolu afin qu'il fonctionne avec d'autres commandes?
command-line
bash
paths
Akiva
la source
la source
vim "$bacon"
Réponses:
Comment faire cela correctement
Tout d'abord, citez toujours vos variables . Ce que vous essayez de faire fonctionne bien si vous le citez correctement:
J'ai conservé le nom de fichier étrange que vous avez choisi ( bien que je ne sache pas pourquoi vous l'avez choisi ) par souci de cohérence.
Maintenant, attribuons le chemin de
pullingATerdon
à une variable, puis essayons d'ouvrir le fichier:Cela échoue, comme prévu. Mais, si nous le citons maintenant correctement:
Cela fonctionne comme prévu. Et oui, vous pouvez également ouvrir le chemin dans un (bon) éditeur:
emacs "$bacon"
cela fonctionnera très bien. OK, etvim
ainsi de suite. Votre choix d'éditeur, bien que malheureux, n'est pas pertinent.Pourquoi le vôtre a échoué
Un moyen rapide de retracer ce qui s'est réellement passé dans votre cas est d'utiliser
set -x
(désactivez-le à nouveau avecset +x
), ce qui oblige le shell à imprimer chaque commande qu'il exécutera avant de l'exécuter. activez les messages de débogage du shell avecset -x
:Cela nous montre que
ls
a été exécuté avec trois arguments distincts:'/home/terdon/foo/\e[92mM@r|<'
,'+'\''|'\''|_e|\|\|0rth'
et'[`-_-"]/pullingATerdon'
. Cela se produit car le shell effectue le fractionnement de mots et l'expansion globale sur les chaînes non entre guillemets. Dans ce cas, le problème est la division des mots, car le shell a vu les espaces dans le chemin d'accès et a lu chaque chaîne séparée par des espaces comme un argument distinct.L'
mkdir
exemple est légèrement différent mais c'est parce que vous nous montrez le message d'erreur de la deuxième invocation de la commande. Je suppose que vous l'avez essayé une fois, puis l'avez exécuté une deuxième fois pour obtenir le résultat de votre question. La première fois que vous l'avez exécuté, cela aurait ressemblé à ceci:Encore une fois, cela tentera de créer trois répertoires, pas un, en raison de la division des mots. Tout d'abord, il a créé (avec succès) le répertoire
/home/terdon/foo/\e[92mM@r|<
:Il a ensuite, également avec succès, créé un répertoire appelé
+'|'|_e|\|\|0rth
dans votre répertoire actuel:Et puis, il a tenté de créer le répertoire
[`-_-"]/pullingATerdon
. Cela a échoué carmkdir
, par défaut, ne crée pas de sous-répertoires (il peut, si vous l'exécutez avec-p
):Étant donné que votre chaîne sans guillemets contenait un
/
,mkdir
considéré comme un chemin d'accès à deux répertoires, a essayé de trouver le premier et a échoué.C'est pourquoi cela a échoué, mais ce qui s'est passé est plus compliqué. La chaîne que vous avez utilisé est en fait un glob shell, en particulier une gamme de glob , qui correspond à tous les fichiers dans le répertoire courant dont le nom est l' un des 5 personnages
`
,-
,_
ou"
. Puisque vous n'avez pas de tels fichiers dans votre répertoire actuel, le glob ne correspond à rien et, comme c'est le comportement par défaut dans bash, se retourne:Pour clarifier, voici ce qui se passe si vous donnez un glob qui correspond à quelque chose:
Le non cité
[p*]
est étendu à la liste des noms de fichiers correspondants (un seul, dans ce cas) et c'est ce qui est transmis àecho
. Encore une autre raison pour laquelle vous devriez citer toutes ces choses.Enfin, l'erreur réelle que vous affichez est la deuxième fois que vous avez exécuté la commande et elle échoue à la première étape, lors de la tentative de création
/home/terdon/foo/\e[92mM@r|<
, car l'invocation précédente avait déjà créé ce répertoire.Plus généralement, chaque fois que vous travaillez avec des noms de fichiers arbitraires, utilisez toujours des globes shell. Des choses comme ça:
Cela fonctionnera pour n'importe quel nom de fichier. Peu importe ce qu'il contient. Dans notre exemple ci-dessus, vous auriez pu faire:
Tout glob qui identifie le fichier cible de manière unique fera l'affaire. De cette façon, vous n'avez pas à vous soucier des caractères spéciaux et vous pouvez simplement laisser le shell les gérer.
Quelques références utiles:
Comment puis-je trouver et gérer en toute sécurité les noms de fichiers contenant des sauts de ligne, des espaces ou les deux? : Une des FAQ sur l'excellent wiki de Gray Cat.
Conséquences pour la sécurité d'oublier de citer une variable dans des shells bash / POSIX : le même post auquel j'ai fait référence au début de cette réponse. Une excellente et très détaillée explication de tout ce qui pourrait mal tourner si vous ne citez pas correctement vos variables shell.
Pourquoi mon script shell s'étouffe-t-il sur les espaces ou autres caractères spéciaux? : tout ce que vous avez toujours voulu savoir sur la gestion des noms de fichiers arbitraires dans le shell.
Quand faut-il citer deux fois? : En savoir plus sur les citations et les variables et, en particulier, sur les quelques cas où vous n'avez pas besoin de les citer
la source