¿Qué caracteres deben escaparse cuando se usa Bash?


¿Hay alguna lista completa de caracteres que deban escaparse en Bash? ¿Se puede comprobar solo con sed?

En particular, estaba comprobando si % necesita ser escapado o no. He intentado

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

Y funcionó bien, sin escapar %. ¿Significa que % no necesita ser escapado? Fue esta una buena manera de comprobar la necesidad?

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

Author: jww, 2013-04-03

7 answers

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

1. Poner toda la cadena entre comillas simples

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

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

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

2. Escapar de cada carácter con una barra invertida

Esto funciona para todos los caracteres excepto nueva línea. Para caracteres de nueva línea utilice single o comillas dobles. Las cadenas vacías todavía deben ser manejadas-reemplazar con ""

\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 Sed: sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

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

Hay un conjunto de caracteres fácil y seguro, como [a-zA-Z0-9,._+:@%/-], que se pueden dejar sin escapar para mantenerlo 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/$/"/'.


Tenga en cuenta que en un programa sed, uno no puede saber si la última línea de entrada termina con un byte de nueva línea (excepto cuando está vacía). Es por eso que ambos arriba las órdenes sed suponen que no. Puede agregar una nueva línea entre comillas manualmente.

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


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

 156
Author: Jo So,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-29 15:34:39

Para salvar a otra persona de tener que RTFM... en bash:

Encerrar 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 ellos (y de la cita en sí, por supuesto) probablemente estés bien.

Si usted toma un enfoque más conservador 'en caso de duda, escapar de ella', debería ser posible evitar obtener en su lugar caracteres con un significado especial no escapando caracteres identificativos (es decir, letras ASCII, números o '_'). Es muy poco probable que estos nunca (es decir, en algún extraño caparazón POSIX-ish) tengan un significado especial y por lo tanto necesitan ser escapados.

 25
Author: Matthew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-18 23:24:34

Formato que se puede reutilizar como entrada de shell

Hay un especial printf directiva de formato (%q) creada para este tipo de solicitud:

Printf [- v var] format [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'

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

Tenga en cuenta que todos los bytes de 128 a 255 tienen que 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 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 hexa de byte, el segundo contiene E si el carácter necesita ser escapado y el tercer campo muestra la presentación del carácter escapado.

¿Por qué ,?

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

Así que no siempre sino 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 
 25
Author: F. Hauri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-17 19:53:15

Los caracteres que necesitan escapar son diferentes en Bourne o POSIX shell que Bash. Generalmente (muy) Bash es un superconjunto de esos proyectiles, por lo que cualquier cosa que escapes en shell debe escaparse en Bash.

Una buena regla general sería "en caso de duda, escapar de ella". Pero escapar de algunos caracteres les da un significado especial, como \n. Estos se enumeran en las páginas man bash bajo Quoting y echo.

Aparte de eso, escapar de cualquier carácter que no es alfanumérico, es más seguro. Yo no conoce una sola lista definitiva.

Las páginas de manual los listan todos en algún lugar, pero no en un solo lugar. Aprender el idioma, esa es la manera de estar seguro.

Uno que me ha atrapado es !. Este es un carácter especial (expansión de la 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.

 17
Author: cdarke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-03 09:53:01

Usando el print '%q' técnica , podemos ejecutar un bucle para averiguar qué caracteres 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 sobre esto.

 15
Author: codeforester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-17 19:52:00

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

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

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

 3
Author: Austin Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-03 09:54:55

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

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

Usando esto, ejecuté algunos experimentos usando caracteres de la tabla ASCII y derivé las siguientes listas:

Caracteres que bash escapa al autocompletar: (incluye espacio)

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

Personajes a los que bash no escapa :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

(Excluí /, como no se puede usar en nombres de directorio)

 3
Author: yuri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-30 02:58:37