¿De qué personajes se debe escapar cuando se usa Bash?

Resuelto fedorqui asked hace 11 años • 7 respuestas

¿Existe alguna lista completa de personajes de los que se debe escapar en Bash? ¿Se puede comprobar solo consed ?

En particular, estaba comprobando si %es necesario escapar o no. Lo intenté

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

y funcionó bien, sin escapar %. Eso significa% que no es necesario escapar? ¿Fue esta una buena manera de comprobar la necesidad?

Y más general: ¿son los mismos personajes para escapar shelly bash?

fedorqui avatar Apr 03 '13 16:04 fedorqui
Aceptado

Hay dos reglas fáciles y seguras que funcionan no sólo en shsino tambiénbash .

1. Pon toda la cadena entre comillas simples.

Esto funciona para todos los caracteres excepto las comillas simples. Para escapar de la comilla simple, cierre la cita anterior, inserte la comilla 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. Escapa de cada carácter con una barra invertida

Esto funciona para todos los caracteres excepto la nueva línea. Para caracteres de nueva línea utilice comillas simples o dobles. Aún se deben manejar cadenas vacías; reemplácelas 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 puede dejar sin formato 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/$/"/'.


Tenga en cuenta que en un programa sed, no se 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 los dos comandos sed anteriores suponen que no es así. Puede agregar una nueva línea citada 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, el binario funciona con la excepción de los bytes NUL (porque las variables se implementan con cadenas C y están destinadas a usarse como cadenas C, es decir, argumentos de programa), pero 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)

Jo So avatar Nov 18 '2013 16:11 Jo So

Formato que se puede reutilizar como entrada de shell

Editar febrero de 2021:intento ${var@Q}

En Bash, puede almacenar su contenido variable con el comando Parameter Expansion@ para la transformación de parámetros :

${parameter@operator}
       Parameter transformation.  The expansion is either a > transforma‐
       tion of the value of parameter or  information  about  parameter
       itself,  depending on the value of operator.  Each operator is a
       single letter:

       Q      The expansion is a string that is the value of  parameter
              quoted in a format that can be reused as input.
...
       A      The  expansion  is  a string in the form of an assignment
              statement or declare command  that,  if  evaluated,  will
              recreate parameter with its attributes and value.

Muestra:

$ var=$'Hello\nGood world.\n'
$ echo "$var"
Hello
Good world.

$ echo "${var@Q}"
$'Hello\nGood world.\n'

$ echo "${var@A}"
var=$'Hello\nGood world.\n'

Antigua respuesta

Existe una directiva de formato especial ( ) creada para este tipo de solicitud:printf%q

printf [-v var] format [arguments]

    %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 se deben utilizar caracteres de escape para todos los bytes del 128 al 255.

Este pequeño bucle imprimirá todos los caracteres desde 0x00hasta 0x7f, utilizando ambos métodos : printf %qy .${var@Q}

for i in {0..127}; do
    printf -v var %02X $i
    printf -v var %b \\x$var
    sign=E
    printf -v res %q "$var"
    [[ $var == "$res" ]] && sign=-
    printf '%02X %s %-*s %-*s\n' $i $sign $(( 31 < i && i < 96 ? 2 : 8
        )) "${res}" $(( 31 < i && i < 96 ? 3 : 8)) "${var@Q}"
done |
    pr -w100 --sep-string='' -t4 |
    sed 's/\o11\+/\o11/g'

Esto debería representar algo como:

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

Dónde

  • el primer campo es el valor hexadecimal del byte,
  • el segundo contiene Esi es necesario escapar del carácter,
  • El tercer espectáculo de campo escapó a la presentación del carácter y
  • El último campo muestra la versión utilizable impresa por ${var@Q}sintaxis.

Pequeña función que busca un grupo limitado de personajes.

Por diversión, aquí hay otra forma de recorrer una cadena, agrupando todos los caracteres según la necesidad de escapar.

specialCharsFromString() {
    local {q,}char bunch{_0,_1} \
        special="${1:-'\`\"/\!@#\$%^&*()-_+={\}[]|;:,.<>? '}"
    while IFS= LANG=C LC_ALL=C read -d '' -rn 1 char; do
        printf -v qchar %q "$char"
        [[ $char == "$qchar" ]]
        local -n bunch=bunch_$?
        bunch+=(${char@Q})
    done < <(printf %s "$special");
    printf 'Characters who %sneed to be escaped:\n%s\n' \
        "doesn't " "${bunch_0[*]}" "" "${bunch_1[*]}"
}
specialCharsFromString $'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
Characters who doesn't need to be escaped:
'@' '%' '-' '_' '+' '=' ':' '.' '/'
Characters who need to be escaped:
'`' '!' '#' '$' '^' '&' '*' '(' ')' '{' '}' '|' '[' ']' '\' ';' \' '"' ',' '<' '>' '?' ' '

Por qué ,?

Podrías ver algunos caracteres de los que no siempre es necesario escapar, ,como }y {.

Así que no siempre pero sí 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

Consulte el capítulo Expansión de llaves enintentopágina de manual de:

  man -P'less +/Brace\ Expansion' bash

Nota sobre el signo de porcentaje% .

No, no es necesario escapar del signo de porcentaje en ningún caso.posix caparazón¡compatible!

Pero encron's crontab!! Este es un problema común ya que la sintaxis utilizada crontabes principalmenteposix caparazóncompatible. pero man -Pless\ +/% 5 crontab:

... Los signos de porcentaje (%) en el comando, a menos que se escapen con una barra invertida (), se cambiarán a caracteres de nueva línea...

Si intenta utilizar la marca de tiempo en crontab, debe escapar %:

* * * * * echo one more line >>file-$(date +\%F).log

Nota sobre el signo de guiones- .

No hay razón para escapar de un guión en una cadena, excepto si intenta usarlos al comienzo de la cadena como primer argumento de un comando normal:

printf '- %3d %s\n' $((count++)) "Some String"
bash: printf: - : invalid option

ls -dashedFilename
ls: invalid option -- 'e'

Incluso escapado , esto no irá mejor:

ls \-dashedFilename
ls: invalid option -- 'e'

Para ello, la forma recomendada es utilizar doble guión --:

De la Directriz 10 de convenciones de servicios públicos de POSIX.1-2017

Directriz 10:

El primer --argumento que no sea un argumento de opción debe aceptarse como delimitador que indica el final de las opciones. Cualquier argumento siguiente debe tratarse como operando, incluso si comienza con el -carácter .

printf  -- '- %3d %s\n' $((count++)) "Some String"
-   2 Some String

ls -- -dashedFilename
ls: cannot access '-dashedFilename': No such file or directory

Vale, no existen.

Alternativa: simplemente evite los guiones al comienzo de cualquier cadena :

printf '\55 %3d %s\n' $((count++)) "Some other String"
-   3 Some other String
printf '\x2d %3d %s\n' $((count++)) "Some other String"
-   4 Some other String

Usando cualquiera de las representaciones octal o hexadecimal del guión

ls ./-dashedFilename
ls: cannot access './-dashedFilename': No such file or directory

Usando ruta relativa o completa.

F. Hauri - Give Up GitHub avatar Jan 07 '2015 10:01 F. Hauri - Give Up GitHub

Usando la print '%q' técnica , podemos ejecutar un bucle para descubrir 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 parecen ,un poco sospechosos. Sería interesante recibir las aportaciones de @CharlesDuffy sobre esto.

codeforester avatar Jun 16 '2017 04:06 codeforester

Para evitar que otra persona tenga que hacer RTFM... en bash :

Al encerrar caracteres entre comillas dobles se conserva el valor literal de todos los caracteres entre 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 misma, por supuesto), probablemente estés bien.

Si adopta un enfoque más conservador de 'en caso de duda, escápese', debería ser posible evitar obtener caracteres con significado especial al no escapar de los caracteres identificadores (es decir, letras ASCII, números o '_'). Es muy poco probable que estos alguna vez (es decir, en algún shell POSIX extraño) tengan un significado especial y, por lo tanto, deban escaparse.

Matthew avatar Mar 03 '2014 23:03 Matthew

Los caracteres que necesitan escape son diferentes en Bourne o POSIX Shell que en Bash. Generalmente (muy) Bash es un superconjunto de esos shells, por lo que cualquier cosa en la que escapes shelldebe escaparse en Bash.

Una buena regla general sería "en caso de duda, evítese". Pero escapar de algunos caracteres les da un significado especial, como \n. Estos se enumeran en las man bashpáginas siguientes Quotingy echo.

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

Las páginas de manual los enumeran todos en alguna parte, pero no en un solo lugar. Aprende el idioma, esa es la manera de estar seguro.

Uno que me ha pillado es !. Este es un carácter especial (expansión histórica) en Bash (y csh) pero no en Korn Shell. Incluso echo "Hello world!"da problemas. El uso de comillas simples, como es habitual, elimina el significado especial.

cdarke avatar Apr 03 '2013 09:04 cdarke