Quais caracteres precisam ser escapados ao usar o Bash?

Existe alguma lista abrangente de caracteres que precisam ser escapados no Bash? Pode ser verificado apenas com sed ?

Em particular, eu estava verificando se % precisa ser escapado ou não. eu tentei

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

e funcionou bem, sem escaping % . Isso significa que % não precisa ser escapado? Foi uma boa maneira de verificar a necessidade?

E mais geral: eles são os mesmos personagens para escaping em shell e bash ?

Existem duas regras fáceis e seguras que funcionam não só no sh mas também no bash .

1. Coloque a string inteira entre aspas simples

Isso funciona para todos os caracteres, exceto a própria cota simples. Para escaping da aspa simples, feche as citações antes, insira a aspa simples e reabra as citações.

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

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

2. Escape cada char com uma barra invertida

Isso funciona para todos os caracteres, exceto newline. Para caracteres de nova linha, use aspas simples ou duplas. Strings vazias ainda devem ser manipuladas – substitua 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 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. Versão mais legível de 2

Há um conjunto de caracteres seguro e fácil, como [a-zA-Z0-9,._+:@%/-] , que pode ser deixado sem escape para mantê-lo mais legível

 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/$/"/' .


Note que em um programa sed, não se pode saber se a última linha de input termina com um byte de nova linha (exceto quando está vazia). É por isso que ambos os comandos sed supõem que não. Você pode adicionar uma nova linha cotada manualmente.

Observe que as variables ​​do shell são definidas apenas para texto no sentido POSIX. O processamento de dados binários não está definido. Para as implementações importantes, o binário funciona com a exceção de bytes NUL (porque as variables ​​são implementadas com strings C e devem ser usadas como strings C, a saber, argumentos de programa), mas você deve alternar para uma localidade “binária” como latin1 .


(Você pode facilmente validar as regras lendo a especificação POSIX para sh . Para o bash, verifique o manual de referência linkado pelo @AustinPhillips)

Para salvar alguém de ter que RTFM … no bash :

Incluir caracteres entre aspas duplas preserva o valor literal de todos os caracteres entre aspas, com exceção de $ , ` , \ e, quando a expansão de histórico é ativada ! .

… então, se você escaping deles (e da citação em si, é claro), você provavelmente está bem.

Se você adotar uma abordagem mais conservadora “em caso de dúvida, escape dela”, deve ser possível evitar, em vez disso, caracteres com significado especial por não escaping de caracteres identificadores (ou seja, letras ASCII, números ou “_”). É muito improvável que eles nunca (em alguns shell POSIX-ish estranho) tenham um significado especial e, portanto, precisam ser escapados.

formato que pode ser reutilizado como input de shell

Existe uma diretiva especial de formato printf ( %q ) criada para esse tipo de solicitação:

formato printf [-v var] [argumentos]

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

Algumas amostras:

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

Isso pode ser usado por meio de variables ​​também:

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

Verificação rápida com todos os (128) bytes ascii:

Note que todos os bytes de 128 a 255 precisam 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 

Isso deve render 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 

Onde o primeiro campo é o valor hexa do byte, o segundo contém E se o caractere precisar ser escapado e o terceiro campo mostrar a apresentação de caractere com escape.

Por quê?

Você pode ver alguns caracteres que nem sempre precisam ser escapados, como, } e { .

Então nem sempre, mas às vezes :

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

ou

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

mas 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 

Os caracteres que precisam de escape são diferentes no shell Bourne ou POSIX do que no Bash. Geralmente (muito) Bash é um superconjunto dessas shells, então qualquer coisa que você escape no shell deve ser escapado no Bash.

Uma boa regra geral seria “em caso de dúvida, fugir”. Mas escaping de alguns personagens dá a eles um significado especial, como \n . Estes são listados nas páginas do man bash sob Quoting e echo .

Fora isso, escaping de qualquer caractere que não seja alfanumérico, é mais seguro. Eu não sei de uma única lista definitiva.

As páginas man listam todas em algum lugar, mas não em um só lugar. Aprenda a língua, essa é a maneira de ter certeza.

Um que me pegou é ! . Este é um caractere especial (expansão de histórico) em Bash (e csh), mas não no shell Korn. Até mesmo echo "Hello world!" dá problemas. Usando aspas simples, como sempre, remove o significado especial.

Usando a técnica de print '%q' , podemos executar um loop para descobrir quais caracteres são especiais:

 #!/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 

Isso dá essa saída:

 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 

Alguns dos resultados parecem suspeitos. Seria interessante obter as informações do @ CharlesDuffy sobre isso.

Eu presumo que você esteja falando de cordas de bash. Existem diferentes tipos de strings que possuem um conjunto diferente de requisitos para escape. por exemplo. As strings de aspas simples são diferentes das strings com aspas duplas.

A melhor referência é a seção Quoting do manual bash.

Explica quais personagens precisam escaping. Observe que alguns caracteres podem precisar de escape dependendo de quais opções estão ativadas, como expansão de histórico.

Percebi que o bash escapa automaticamente de alguns caracteres ao usar o preenchimento automático.

Por exemplo, se você tiver um diretório chamado dir:A , o bash será preenchido automaticamente para dir\:A

Usando isso, eu executei algumas experiências usando caracteres da tabela ASCII e obtive as seguintes listas:

Caracteres que o bash escapa no preenchimento automático : (inclui espaço)

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

Personagens que o bash não escapa :

 #%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ 

(Eu excluí / , como não pode ser usado em nomes de diretório)