J'ai eu ce comportement étrange ce matin dans un terminal bash:
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
bash: [: missing «]»
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
true
Après quelques recherches, je découvre que supprimer le 30ème caractère (l'espace entre client.conf et "]") et le remplacer par un espace a permis à la commande de fonctionner à nouveau.
Mon hypothèse était juste: un caractère vide inconnu a été inséré dans la commande , mais la question est la suivante:
BTW, je suis sous Ubuntu 18.04/Langue française, le script à partir duquel je collez la commande est sur une clé USB et a peut-être été modifié sous Windows.
Merci pour vos très bonnes réponses. Le caractère incorrect est un caractère UTF-8 d’espace insécable c2 a0 . La question Comment supprimer le caractère spécial 'M-BM-' avec sed présente un fait intéressant à propos de ce caractère.
La chose étrange est que le script est libre de ce personnage. Donc je ne sais pas d'où ça vient.
Une option consiste à examiner les caractères que vous essayez d'utiliser avec un visualiseur ou un éditeur hexadécimal. hexdump
est une bonne option si vous êtes limité au terminal.
$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000 5b 20 2d 66 20 2f 65 74 63 2f 6f 70 65 6e 76 70 |[ -f /etc/openvp|
00000010 6e 2f 63 6c 69 65 6e 74 2e 63 6f 6e 66 20 5d 20 |n/client.conf ] |
00000020 26 26 20 65 63 68 6f 20 74 72 75 65 0a |&& echo true.|
0000002d
Vous pouvez voir ici que les noms space
, close-square-brace
, space
sont corrects - 0x20
, 0x5D
, 0x20
.
Ces valeurs sont des codes ASCII, affichés sous la forme hexadécimal . Toute valeur en dehors de la plage 0x20
- 0x7E
n'est pas un "caractère imprimable" en ce qui concerne ASCII, et ne jouera probablement pas bien avec des interfaces de ligne de commande.
Remarque: J'ai copié votre première ligne "cassée" à utiliser dans l'exemple hexdump
ci-dessus, de sorte que quelque chose a remplacé le not-an-ASCII-space = avec un ASCII espace entre votre source d'origine et votre question rendue.
Pour répéter cela, suivez les étapes suivantes:
hexdump -Cv <<"EOF"
et appuyez sur EnterEOF
sur une ligne distincte, puis appuyez sur EnterLes terminaux et les interfaces de ligne de commande ne gèrent pas bien les caractères spéciaux - comme vous l'avez découvert. Si vous ne faites pas très attention à la mise en forme des documents, vous aurez également des problèmes avec Microsoft Word (et d'autres) avec "smart quotes", tirets, la liste est longue ...
Trouvez la différence: (le haut est "citations intelligentes", le bas est "citations droites")
$ hexdump -Cv <<"EOF"
> “quoted string”
> EOF
00000000 e2 80 9c 71 75 6f 74 65 64 20 73 74 72 69 6e 67 |...quoted string|
00000010 e2 80 9d 0a |....|
00000014
Ici, les guillemets ouverts ne sont pas une simple citation ASCII ("
), mais sont un Unicode/ TF-8 série - 0xE2
, 0x80
, 0x9C
ou U+201C
- que le terminal ne gérera pas comme prévu.
La suggestion de Kiwy de cat -A
fait aussi le travail:
$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$
Remarque: En utilisant echo "..." | hd
, vous avez une chance que bash substitue des parties de la chaîne que vous essayez d'inspecter. Cela est particulièrement préoccupant lorsque vous essayez d'inspecter les composants d'un script.
Par exemple essayez:
$ echo "${USER}"
attie
$ echo "`whoami`"
attie
$ echo "$(whoami)"
attie
$ cat <<EOF
> ${USER}
> EOF
attie
Ces méthodes remplacent les composants par le texte correspondant. Pour éviter cela, utilisez l’une des approches suivantes. Notez l'utilisation de guillemets simples ('
), et d'un "[heredoc cité" ("EOF"
).
$ echo '${USER}'
${USER}
$ echo '`whoami`'
`whoami`
$ echo '$(whoami)'
$(whoami)
$ cat <<"EOF"
> ${USER}
> EOF
${USER}
Vous pouvez utiliser cat
avec l'option -A
: du manuel:
-A, --show-all
equivalent to -vET
-E, --show-ends
display $ at end of each line
-T, --show-tabs
display TAB characters as ^I
-v, --show-nonprinting
use ^ and M- notation, except for LFD and TAB
Donc, cat -A yourscrip.sh
vous montrera des personnages invisibles et étranges.
echo "<your command>" | hd
devrait fonctionner. Recherchez un retour arrière (0x08) ou des caractères avec des codes> = 80. echo "<your command>" | wc -b
et vérifier que le nombre correspond à ce que vous voyez est également une bonne idée.
Copier des fichiers à partir de fichiers contenant "Office" dans son nom est dangereux, car ce logiciel permet souvent de remplacer des caractères: en français, recherchez les guillemets remplacés par "guillemets", en anglais pour les guillemets simples, remplacés par équivalent ouvert/fermé. Le plus difficile que j'ai jamais trouvé était un espace insécable de 0 largeur au milieu d'un nom de fichier (3 jours d'indisponibilité du serveur ...).
Bash et d'autres shells comme zsh peuvent ouvrir la ligne de commande actuelle dans un éditeur. Le raccourci par défaut pour bash est C-x C-e
(CtrlXCtrlE), et il s’ouvre dans le premier disponible de $VISUAL
, $EDITOR
et emacs. En pratique, cela est inestimable pour le débogage et la modification de commandes complexes. Selon la façon dont vous le regardez, zsh est plus convivial que bash ici: lorsque l'éditeur quitte, bash exécute la commande immédiatement, alors que zsh attend que vous appuyiez sur Enter (vous donnant plus de chances de modifier la commande).
Après avoir ouvert la commande dans un éditeur, vous pouvez configurer vos éditeurs pour afficher différemment les caractères non-ASCII.
Par exemple, avec Vim , en utilisant les paramètres suivants:
set encoding=latin1
set isprint=
set display+=uhex
Ou, en adaptant les méthodes des autres réponses:
bash-4.4$ f() { cat -A "$@"; false; } # exit false to prevent bash from running the command
bash-4.4$ VISUAL=f
bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true # C-x C-e here
[ -f /etc/openvpn/client.confM-BM- ] && echo true$