web-dev-qa-db-fra.com

Comment puis-je vérifier les caractères textuels d'une chaîne de commande bash?

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
  • La première commande était collée à partir d'un script édité avec gedit.
  • Le second était tapé directement dans le terminal.

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:

  1. Comment puis-je révéler ces caractères dans le terminal afin de pouvoir déboguer la commande? Et plus important:
  2. Comment puis-je empêcher que cela ne se reproduise?

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.

15
Gabriel Glenn

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:

  1. Tapez hexdump -Cv <<"EOF" et appuyez sur Enter
  2. Collez le texte que vous souhaitez utiliser
  3. Saisissez EOF sur une ligne distincte, puis appuyez sur Enter

Les 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")

example of smart quotes vs straight quotes

$ 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}
11
Attie

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.

18
Kiwy

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 ...).

9
xenoid

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

enter image description here

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$
2
muru