Comment faire cela correctement
Tout d'abord, citez toujours vos variables . Ce que vous essayez de faire fonctionne bien si vous le citez correctement:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
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:
$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
Cela échoue, comme prévu. Mais, si nous le citons maintenant correctement:
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
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, et vim
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 avec set +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 avec set -x
:
$ set -x
$ /bin/ls $bacon
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
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:
$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory
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|<
:
$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'
Il a ensuite, également avec succès, créé un répertoire appelé +'|'|_e|\|\|0rth
dans votre répertoire actuel:
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
Et puis, il a tenté de créer le répertoire [`-_-"]/pullingATerdon
. Cela a échoué car mkdir
, par défaut, ne crée pas de sous-répertoires (il peut, si vous l'exécutez avec -p
):
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
É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:
$ echo "[\`-_-\"]/pullingATerdon" ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon' ## but it echoes the right thing
[`-_-"]/pullingATerdon ## and matches nothing, so returns itself.
Pour clarifier, voici ce qui se passe si vous donnez un glob qui correspond à quelque chose:
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
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:
for file in *; do command "$file"; done
Cela fonctionnera pour n'importe quel nom de fichier. Peu importe ce qu'il contient. Dans notre exemple ci-dessus, vous auriez pu faire:
emacs /home/terdon/*92mM*/pullingATerdon
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
vim "$bacon"