¿De qué personajes se debe escapar cuando se usa Bash?
¿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 shell
y bash
?
Hay dos reglas fáciles y seguras que funcionan no sólo en sh
sino 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)
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 0x00
hasta 0x7f
, utilizando ambos métodos : printf %q
y .${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
E
si 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 crontab
es 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.
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.
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.
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 shell
debe 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 bash
páginas siguientes Quoting
y 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.