¿Cómo puedo verificar los caracteres literales de una cadena de comando bash?

15

Tuve este comportamiento extraño esta mañana en una terminal de 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
  • El primer comando fue pegado de un script editado con gedit.
  • El segundo fue escrito directamente en la terminal.

Después de investigar un poco, descubrí que al eliminar el carácter 30 (el espacio entre client.conf y "]") y reemplazarlo con un espacio, el comando funciona nuevamente.

Mi suposición era correcta: un carácter en blanco desconocido se deslizó en el comando , pero la pregunta es:

  1. ¿Cómo puedo revelar esos caracteres en la terminal para poder depurar el comando? Y más importante:
  2. ¿Cómo puedo evitar que esto vuelva a suceder?

Por cierto, ejecuto Ubuntu 18.04 / idioma francés, el script desde el que pego el comando está en una unidad USB y puede que también haya sido editado en Windows.


Gracias por tus muy buenas respuestas. El carácter incorrecto es un carácter UTF-8 de espacio c2 a0 que no se rompe. La pregunta Cómo eliminar el carácter especial 'M-BM-' con sed tiene un hecho interesante sobre ese personaje.

Lo extraño es que el guión está libre de este personaje. Entonces no sé de dónde vino.

Gabriel Glenn
fuente
3
Use un editor que resalte dichos personajes. El resaltado de sintaxis también ayuda mucho. Nunca pegue directamente desde la web al terminal, vaya siempre al editor mencionado anteriormente.
choroba
2
Es posible que desee encontrar el comando del problema en su lista de historial, luego canalizar la salida a través de un programa de visualización hexadecimal. Para que no necesite pasar por una lista larga, vuelva a ejecutar el comando para colocarlo en la parte inferior de la lista del historial y ejecútelo history 2|xxd(porque el historycomando en sí siempre es el último en la lista), o escriba history|grep "CommandWithProblem"|xxd. Puede usar cualquier otro programa de visualización hexadecimal en lugar de xxd, pero esto por defecto es un formato que me gusta.
AFH
@Gabriel Glenn, marque la respuesta mejor / más útil / lo que sea como " aceptada " utilizando la marca, en lugar de comentar en cada una que la respuesta ayudó. info
Attie
1
@Attie, sí lo haré, solo suelo esperar 24 horas antes de aceptar las mejores respuestas, como se sugiere en: meta.stackexchange.com/questions/5234/…
Gabriel Glenn
1
En lo personal me gustaría usar set -x. Esto le mostrará el comando y cómo se divide. No necesariamente diría "mal personaje aquí", pero te mostraría que bash no se estaba dividiendo en ese personaje.
Patrick

Respuestas:

11

Una opción es mirar los caracteres que está intentando usar con un visor o editor hexadecimal. hexdumpes una buena opción si está limitado al 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

Se puede ver aquí que el space, close-square-brace, spaceson correctas - 0x20, 0x5D, 0x20.

Estos valores son códigos ASCII, que se muestran en hexadecimal . Cualquier valor fuera del rango 0x20- 0x7Eno es un " carácter imprimible " en lo que se refiere a ASCII, y lo más probable es que no va a jugar bien con interfaces de línea de comandos.

Nota: Copié su primera línea " rota " para usar en el hexdumpejemplo anterior, por lo que algo ha reemplazado el espacio no ASCII con un espacio ASCII entre su fuente original y su pregunta representada.


Para repetir esto, siga los siguientes pasos:

  1. Escribe hexdump -Cv <<"EOF"y presionaEnter
  2. Pega el texto que te gustaría usar
  3. Escriba EOFen una línea propia y presioneEnter

Los terminales y las interfaces de línea de comandos no manejan bien los caracteres especiales, como ha descubierto. Si no tiene mucho cuidado con el formato de los documentos, también tendrá problemas con Microsoft Word (y otros) al usar " comillas inteligentes ", guiones, la lista continúa ...

Encuentra la diferencia: (la parte superior es " comillas inteligentes ", la parte inferior es " comillas rectas ")

ejemplo de comillas inteligentes vs comillas rectas

$ 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

En este caso, las cotizaciones abiertas no son una cita simple ASCII ( "), pero son unos Unicode / UTF-8 serie - 0xE2, 0x80, 0x9C, o U+201C- que el terminal no manejará como se podría esperar.

La sugerencia de Kiwy de cat -Atambién hace el trabajo:

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

Nota: cuando se usaecho "..." | hd, existe la posibilidad de que bash sustituya partes de la cadena que está tratando de inspeccionar. Esto es particularmente preocupante cuando se intenta inspeccionar los componentes de un script.

Por ejemplo, intente:

$ echo "${USER}"
attie

$ echo "`whoami`"
attie

$ echo "$(whoami)"
attie

$ cat <<EOF
> ${USER}
> EOF
attie

Estos métodos están reemplazando componentes con el texto relevante. Para evitar esto, utilice uno de los siguientes enfoques. Tenga en cuenta el uso de comillas simples ( ') y un " heredoc citado " ( "EOF").

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

$ echo '`whoami`'
`whoami`

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

$ cat <<"EOF"
> ${USER}
> EOF
${USER}
Attie
fuente
Esta solución funciona: echo "[ -f /etc/openvpn.ovpn ]" | hd devoluciones [...] c2 a0 [...]. Podemos ver el espacio ininterrumpido del personaje c2 a0 UT-8
Gabriel Glenn
18

Puede usar catcon la -Aopción: del manual:

   -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

Entonces cat -A yourscrip.shte mostrará personajes invisibles y extraños.

Kiwy
fuente
77
Esta solución funciona: echo "[ -f /etc/openvpn.ovpn ]" | cat -Adevoluciones [ -f /etc/openvpn/client.ovpnM-BM- ]$. Podemos ver el espacio ininterrumpido del personaje M-BM- UT-8
Gabriel Glenn
@GabrielGlenn me alegra que esto te haya ayudado.
Kiwy
9

echo "<your command>" | hdDeberia trabajar. Busque retroceso (0x08) o caracteres con códigos> = 80. echo "<your command>" | wc -by comprobar que el recuento coincide con lo que ves también es una buena idea.

Copiar cosas de archivos producidos por cualquier cosa con "Office" en su nombre es peligroso, porque dicho software a menudo se toma la libertad de reemplazar caracteres: en francés, busque las comillas dobles reemplazadas por "guillemets", en inglés las comillas simples reemplazadas por sus abrir / cerrar equivalentes. El más difícil que encontré fue un espacio sin interrupciones de ancho 0 en el medio del nombre de un archivo (3 días de tiempo de inactividad del servidor ...).

xenoide
fuente
2
Vale la pena mencionar que hdes breve, hexdumpque también se menciona en la respuesta de Attie.
Mikael Kjær
@ MikaelKjær: en Ubuntu, hdes equivalente a hexdump -C.
AFH
1
@xenoid: dije 'editado en Windows', no editado con un escritor de Office, no estamos locos;). Si fue editado, fue con Notepad ++.
Gabriel Glenn
1
Esta solución funciona: echo "[ -f /etc/openvpn.ovpn ]" | hd devoluciones [...] c2 a0 [...]. Podemos ver el espacio sin interrupción del personaje c2 a0 UT-8
Gabriel Glenn
2

Bash y otros shells como zsh pueden abrir la línea de comando actual en un editor. El acceso directo predeterminado para bash es C-x C-e( CtrlX CtrlE), y se abre en el primer disponible de $VISUAL, $EDITORy emacs. En la práctica, esto es invaluable para depurar y modificar comandos complejos. Dependiendo de cómo lo mire, zsh es más amigable que bash aquí: cuando el editor sale, bash ejecuta el comando inmediatamente, mientras que zsh espera a que presione Enter(lo que le brinda más oportunidades de editar el comando).

Después de abrir el comando en un editor, puede configurar sus editores para mostrar caracteres no ASCII de manera diferente.

Por ejemplo, con Vim , usando esta configuración:

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

ingrese la descripción de la imagen aquí

O, adaptando los métodos de otras respuestas:

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
fuente