¿Qué personajes necesitan escaparse cuando usas Bash?

¿Hay alguna lista completa de los personajes que deben escaparse en Bash? ¿Se puede verificar solo con sed ?

En particular, estaba verificando si el % necesita ser escapado o no. Lo intenté

 echo "h%h" | sed 's/%/i/g' 

y funcionó bien, sin escapar % . ¿Significa que no se necesita escapar a % ? ¿Era esta una buena manera de verificar la necesidad?

Y más general: ¿son los mismos personajes para escapar en shell y bash ?

Hay dos reglas fáciles y seguras que funcionan no solo en sh sino también en bash .

1. Pon toda la cadena entre comillas simples

Esto funciona para todos los caracteres excepto la comilla simple. Para escapar de la comilla simple, cierre la cita anterior, inserte la comilla simple y vuelva a abrir la cita.

 'I'\''mas@fe $tring which ends in newline ' 

comando sed: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. Escape cada char con una barra invertida

Esto funciona para todos los personajes excepto Newline. Para caracteres de nueva línea, use comillas simples o dobles. Todavía se deben manipular las cadenas vacías; reemplácelas por ""

 \I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e" " 

comando de sed: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' .

2b. Versión más legible de 2

Hay un juego de caracteres fácil y seguro, como [a-zA-Z0-9,._+:@%/-] , que puede dejarse sin guardar para que sea más legible.

 I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline" " 

comando sed: LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/' .


Tenga en cuenta que en un progtwig sed, uno no puede saber si la última línea de entrada termina con un byte de nueva línea (excepto cuando está vacío). Es por eso que los dos comandos sed anteriores asumen que no. Puede agregar una línea nueva cotizada manualmente.

Tenga en cuenta que las variables de shell solo están definidas para texto en el sentido POSIX. El procesamiento de datos binarios no está definido. Para las implementaciones que importan, el binario funciona con la excepción de los bytes NUL (porque las variables se implementan con cadenas C, y se deben usar como cadenas C, es decir, argumentos de progtwig), pero se debe cambiar a una configuración regional “binaria” como latin1 .


(Puede validar fácilmente las reglas leyendo la especificación POSIX para sh . Para bash, consulte el manual de referencia vinculado por @AustinPhillips)

Para salvar a alguien más de tener que RTFM … en bash :

La inclusión de caracteres entre comillas dobles conserva el valor literal de todos los caracteres dentro de las comillas, con la excepción de $ , ` , \ y, cuando la expansión del historial está habilitada ! .

… así que si escapas de eso (y la cita en sí misma, por supuesto) probablemente estés bien.

Si adopta un enfoque más conservador “en caso de duda, escape”, debería ser posible evitar tener caracteres con un significado especial al no escapar de los caracteres identificadores (es decir, letras ASCII, números o ‘_’). Es muy poco probable que alguna vez (es decir, en algún extraño caparazón POSIX-ISH) tenga un significado especial y por lo tanto necesite ser escapado.

formato que se puede reutilizar como entrada de shell

Hay una directiva especial de formato de impresión ( %q ) creada para este tipo de solicitud:

formato printf [-v var] [argumentos]

  %q causes printf to output the corresponding argument in a format that can be reused as shell input. 

Algunas muestras:

 read foo Hello world printf "%q\n" "$foo" Hello\ world printf "%q\n" $'Hello world!\n' $'Hello world!\n' 

Esto también podría usarse a través de variables:

 printf -v var "%q" "$foo " echo "$var" $'Hello world\n' 

Verificación rápida con todos los (128) bytes ascii:

Tenga en cuenta que todos los bytes de 128 a 255 deben ser escapados.

 for i in {0..127} ;do printf -v var \\%o $i printf -v var $var printf -v res "%q" "$var" esc=E [ "$var" = "$res" ] && esc=- printf "%02X %s %-7s\n" $i $esc "$res" done | column 

Esto debe representar algo así como:

 00 E '' 1A E $'\032' 34 - 4 4E - N 68 - h 01 E $'\001' 1B E $'\E' 35 - 5 4F - O 69 - i 02 E $'\002' 1C E $'\034' 36 - 6 50 - P 6A - j 03 E $'\003' 1D E $'\035' 37 - 7 51 - Q 6B - k 04 E $'\004' 1E E $'\036' 38 - 8 52 - R 6C - l 05 E $'\005' 1F E $'\037' 39 - 9 53 - S 6D - m 06 E $'\006' 20 E \ 3A - : 54 - T 6E - n 07 E $'\a' 21 E \! 3B E \; 55 - U 6F - o 08 E $'\b' 22 E \" 3C E \< 56 - V 70 - p 09 E $'\t' 23 E \# 3D - = 57 - W 71 - q 0A E $'\n' 24 E \$ 3E E \> 58 - X 72 - r 0B E $'\v' 25 - % 3F E \? 59 - Y 73 - s 0C E $'\f' 26 E \& 40 - @ 5A - Z 74 - t 0D E $'\r' 27 E \' 41 - A 5B E \[ 75 - u 0E E $'\016' 28 E \( 42 - B 5C E \\ 76 - v 0F E $'\017' 29 E \) 43 - C 5D E \] 77 - w 10 E $'\020' 2A E \* 44 - D 5E E \^ 78 - x 11 E $'\021' 2B - + 45 - E 5F - _ 79 - y 12 E $'\022' 2C E \, 46 - F 60 E \` 7A - z 13 E $'\023' 2D - - 47 - G 61 - a 7B E \{ 14 E $'\024' 2E - . 48 - H 62 - b 7C E \| 15 E $'\025' 2F - / 49 - I 63 - c 7D E \} 16 E $'\026' 30 - 0 4A - J 64 - d 7E E \~ 17 E $'\027' 31 - 1 4B - K 65 - e 7F E $'\177' 18 E $'\030' 32 - 2 4C - L 66 - f 19 E $'\031' 33 - 3 4D - M 67 - g 

Donde el primer campo es el valor hexadecimal del byte, el segundo contiene E si el carácter necesita ser escapado y el tercer campo muestra la presentación escapada del carácter.

¿Por qué?

Puede ver algunos caracteres que no siempre necesitan ser escapados, como } y { .

Así que no siempre, pero en algún momento :

 echo test 1, 2, 3 and 4,5. test 1, 2, 3 and 4,5. 

o

 echo test { 1, 2, 3 } test { 1, 2, 3 } 

pero cuidado:

 echo test{1,2,3} test1 test2 test3 echo test\ {1,2,3} test 1 test 2 test 3 echo test\ {\ 1,\ 2,\ 3\ } test 1 test 2 test 3 echo test\ {\ 1\,\ 2,\ 3\ } test 1, 2 test 3 

Los caracteres que necesitan escaparse son diferentes en shell Bourne o POSIX que Bash. Generalmente (muy) Bash es un superconjunto de esas conchas, por lo que todo lo que escapas con shell debería escapar en Bash.

Una buena regla general sería “si tienes dudas, escápate”. Pero escapar de algunos personajes les da un significado especial, como \n . Estos se enumeran en las páginas man bash en Quoting y echo .

Aparte de eso, escapa de cualquier carácter que no sea alfanumérico, es más seguro. No sé de una sola lista definitiva.

Las páginas man las listan todas en algún lado, pero no en un solo lugar. Aprende el idioma, esa es la manera de estar seguro.

¡Uno que me ha sorprendido es ! . Este es un personaje especial (expansión de historia) en Bash (y csh) pero no en Korn shell. Incluso echo "Hello world!" da problemas El uso de comillas simples, como de costumbre, elimina el significado especial.

Usando la técnica de print '%q' , podemos ejecutar un ciclo para descubrir qué personajes son especiales:

 #!/bin/bash special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ ' for ((i=0; i < ${#special}; i++)); do char="${special:i:1}" printf -v q_char '%q' "$char" if [[ "$char" != "$q_char" ]]; then printf 'Yes - character %s needs to be escaped\n' "$char" else printf 'No - character %s does not need to be escaped\n' "$char" fi done | sort 

Da esta salida:

 No, character % does not need to be escaped No, character + does not need to be escaped No, character - does not need to be escaped No, character . does not need to be escaped No, character / does not need to be escaped No, character : does not need to be escaped No, character = does not need to be escaped No, character @ does not need to be escaped No, character _ does not need to be escaped Yes, character needs to be escaped Yes, character ! needs to be escaped Yes, character " needs to be escaped Yes, character # needs to be escaped Yes, character $ needs to be escaped Yes, character & needs to be escaped Yes, character ' needs to be escaped Yes, character ( needs to be escaped Yes, character ) needs to be escaped Yes, character * needs to be escaped Yes, character , needs to be escaped Yes, character ; needs to be escaped Yes, character < needs to be escaped Yes, character > needs to be escaped Yes, character ? needs to be escaped Yes, character [ needs to be escaped Yes, character \ needs to be escaped Yes, character ] needs to be escaped Yes, character ^ needs to be escaped Yes, character ` needs to be escaped Yes, character { needs to be escaped Yes, character | needs to be escaped Yes, character } needs to be escaped 

Algunos de los resultados, como , parecen un poco sospechosos. Sería interesante obtener las aportaciones de @ CharlesDuffy en esto.

Supongo que estás hablando de cadenas de bash. Hay diferentes tipos de cadenas que tienen un conjunto diferente de requisitos para escapar. p.ej. Las cadenas de comillas simples son diferentes de las cadenas de comillas dobles.

La mejor referencia es la sección de citas del manual de bash.

Explica qué personajes necesitan escapar. Tenga en cuenta que algunos caracteres pueden necesitar escaparse dependiendo de qué opciones están habilitadas, como la expansión del historial.

Me di cuenta de que bash escapa automáticamente de algunos caracteres cuando usa autocompletar.

Por ejemplo, si tiene un directorio llamado dir:A , bash se autocompletará en dir\:A

Al usar esto, realicé algunos experimentos usando caracteres de la tabla ASCII y obtuve las siguientes listas:

Caracteres que Bash escapa en autocompletar : (incluye espacio)

  !"$&'()*,:;<=>?@[\]^`{|} 

Personajes que bash no escapa :

 #%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ 

(He excluido / , ya que no se puede usar en nombres de directorio)