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

15

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 a été collée à partir d'un script édité avec gedit.
  • Le second a été tapé directement dans le terminal.

Après quelques recherches, je découvre qu'en supprimant le 30ème caractère (l'espace entre client.conf et "]") et en le remplaçant par un espace, la commande a de nouveau fonctionné.

Mon hypothèse était juste: un caractère vierge inconnu s'est glissé dans la commande , mais la question est:

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

BTW, j'exécute Ubuntu 18.04 / langue française, le script à partir duquel je colle la commande est dans un lecteur USB et peut avoir été modifié sur Windows aussi.


Merci pour vos très bonnes réponses. Le mauvais caractère 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 a un fait intéressant à propos de ce caractère.

Ce qui est étrange, c'est que le script est exempt de ce personnage. Donc je ne sais pas d'où ça vient.

Gabriel Glenn
la source
3
Utilisez un éditeur qui met en évidence ces caractères. La mise en évidence de la syntaxe est également très utile. Ne collez jamais directement du Web vers le terminal, passez toujours par l'éditeur susmentionné.
choroba
2
Vous voudrez peut-être trouver la commande de problème dans votre liste d'historique, puis diriger la sortie via un programme d'affichage hexadécimal. Pour que vous n'ayez pas à parcourir une longue liste, réexécutez la commande pour la placer au bas de la liste d'historique et exécutez history 2|xxd(car la historycommande elle-même est toujours la dernière de la liste), ou tapez history|grep "CommandWithProblem"|xxd. Vous pouvez utiliser n'importe quel autre programme d'affichage hexadécimal au lieu de xxd, mais cela correspond par défaut à un format que j'aime.
AFH
@Gabriel Glenn, veuillez marquer la meilleure réponse / la plus utile / quelle que soit la réponse comme " acceptée " à l'aide de la coche - plutôt que de commenter chacune des réponses que la réponse a aidé. info
Attie
1
@Attie, Oui je le ferai, j'attends généralement 24 heures avant d'accepter les meilleures réponses, comme suggéré dans: meta.stackexchange.com/questions/5234/…
Gabriel Glenn
1
Personnellement, j'utiliserais set -x. Cela vous montrerait la commande et comment elle est divisée. Cela ne signifierait pas nécessairement «mauvais personnage ici», mais cela vous montrerait que bash ne se divisait pas sur ce personnage.
Patrick

Réponses:

11

Une option consiste à regarder les personnages que vous essayez d'utiliser avec une visionneuse ou un éditeur hexadécimal. hexdumpest 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 space, close-square-brace, spacesont corrects - 0x20, 0x5D, 0x20.

Ces valeurs sont des codes ASCII, affichés en hexadécimal . Toute valeur en dehors de la plage 0x20- 0x7En'est pas un " caractère imprimable " en ce qui concerne ASCII, et ne fonctionnera probablement pas bien avec les interfaces de ligne de commande.

Remarque: J'ai copié votre première ligne " brisée " pour l'utiliser dans lehexdump exemple ci-dessus, donc quelque chose a remplacé l' espace non-ASCII par un espace ASCII entre votre source d'origine et votre question rendue.


Pour répéter cela, procédez comme suit:

  1. Type hexdump -Cv <<"EOF" et appuyez surEnter
  2. Collez le texte que vous souhaitez utiliser
  3. Saisissez EOFvotre propre ligne et appuyez surEnter

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 n'êtes pas très prudent avec la mise en forme des documents, vous aurez également des problèmes avec Microsoft Word (et d'autres) en utilisant " guillemets intelligents ", des tirets em, la liste continue ...

Repérez la différence: (le haut est " citations intelligentes ", le bas est " citations droites ")

exemple de citations intelligentes vs 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 citations ouvertes ne sont pas une citation simple ASCII ( "), mais sont un Unicode / UTF-8 série - 0xE2, 0x80,0x9C ouU+201C - que le terminal ne traitera pas comme on pouvait s'y attendre.

La suggestion de Kiwy fait cat -Aégalement l'affaire:

$ cat -A <<"EOF"
> quoted string
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$

Remarque: lors de l'utilisation 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 " hérédoc entre guillemets " ( "EOF").

$ echo '${USER}'
${USER}

$ echo '`whoami`'
`whoami`

$ echo '$(whoami)'
$(whoami)

$ cat <<"EOF"
> ${USER}
> EOF
${USER}
Attie
la source
Cette solution fonctionne: echo "[ -f /etc/openvpn.ovpn ]" | hd retourne [...] c2 a0 [...]. Nous pouvons voir l' espace insécable du caractère C2 A0 UT-8
Gabriel Glenn
18

Vous pouvez utiliser catavec l' -Aoption: 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

Alors cat -A yourscrip.shvous montrer des personnages invisibles et étranges.

Kiwy
la source
7
Cette solution fonctionne: echo "[ -f /etc/openvpn.ovpn ]" | cat -Aretourne [ -f /etc/openvpn/client.ovpnM-BM- ]$. Nous pouvons voir l' espace insécable du personnage M-BM- UT-8
Gabriel Glenn
@GabrielGlenn heureux que cela vous ait aidé.
Kiwy
9

echo "<your command>" | hddevrait marcher. Recherchez le retour arrière (0x08) ou les caractères avec des codes> = 80.echo "<your command>" | wc -bet vérifier que le nombre correspond à ce que vous voyez est également une bonne idée.

Copier des trucs à partir de fichiers produits par n'importe quoi avec "Office" dans son nom est dangereux, car un tel logiciel prend souvent la liberté de remplacer les caractères: en français, recherchez les guillemets remplacés par "guillemets", en anglais pour les guillemets simples remplacés par leur ouvrir / fermer des équivalents. Le plus difficile que j'ai jamais trouvé était un espace insécable de largeur 0 au milieu d'un nom de fichier (3 jours d'arrêt du serveur ...).

xénoïde
la source
2
Il convient de mentionner que hdc'est court pour hexdumpce qui est également mentionné dans la réponse d'Attie.
Mikael Kjær
@ MikaelKjær - Sur Ubuntu, hdest équivalent à hexdump -C.
AFH
1
@xenoid: J'ai dit 'édité sur Windows', pas édité avec un Office Writer, nous ne sommes pas fous;). S'il était édité, c'était avec Notepad ++.
Gabriel Glenn
1
Cette solution fonctionne: echo "[ -f /etc/openvpn.ovpn ]" | hd retourne [...] c2 a0 [...]. Nous pouvons voir l' espace insécable du caractère C2 A0 UT-8
Gabriel Glenn
2

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( CtrlX CtrlE), et il s'ouvre dans le premier disponible de $VISUAL, $EDITORet 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 se ferme, bash exécute la commande immédiatement, tandis que zsh attend que vous appuyiez 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 ces paramètres:

set encoding=latin1
set isprint=
set display+=uhex

entrez la description de l'image ici

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$
muru
la source