Quais caracteres precisam ser escapados nos argumentos da linha de comando?

14

No Bash, ao especificar argumentos de linha de comando para um comando, quais caracteres precisam ser escapados?

Eles são limitados aos metacharacters de Bash: espaço, guia, |, &, ;, (, ), <, e >?

Tim
fonte
Não se esqueça (possível) nome do arquivo brilhando com * e?
Jeff Schaller
Obrigado. Você poderia listar exaustivamente os tipos de caracteres que precisam ser escapados nos argumentos da linha de cmd?
Tim
É bom ter uma lista, mas a coisa mais importante a entender sobre a citação é: Tudo entre aspas simples é passado literalmente e sem divisão de palavras. Sem exceções. (Isto significa que não há nenhuma maneira que seja para incorporar uma única citação dentro de aspas simples, por sinal, mas isso é fácil de contornar .)
Wildcard

Respostas:

22

Os seguintes caracteres têm um significado especial para o próprio shell em alguns contextos e podem precisar ser ignorados nos argumentos:

Alguns desses personagens são usados ​​para mais coisas e em mais lugares do que o que eu vinculei.


Existem alguns casos de canto explicitamente opcionais:


Escapar de uma nova linha requer aspas - as barras invertidas não farão o trabalho. Quaisquer outros caracteres listados no IFS precisarão de tratamento semelhante. Você não precisa escapar ]ou }, mas você não precisa escapar )porque é um operador.

Alguns desses personagens têm limites mais rígidos quando realmente precisam escapar do que outros. Por exemplo, a#bestá ok, mas a #bé um comentário, enquanto >precisaria escapar nos dois contextos. De qualquer maneira, não custa escapar de todos eles de maneira conservadora, e é mais fácil do que lembrar as finas distinções.

Se o seu próprio nome de comando é uma palavra-chave shell ( if, for, do), então você precisa escapar ou citá-lo também. O único interessante é que in, porque não é óbvio que é sempre uma palavra-chave. Você não precisa fazer isso para palavras-chave usadas em argumentos, somente quando você (tolamente!) Nomeou um comando após um deles. Os operadores de shell ( (, &etc) sempre precisam citar onde quer que estejam.


1 Stéphane observou que qualquer outro caractere em branco de byte único da sua localidade também precisa ser escapado. Nos locais mais comuns e sensíveis, pelo menos aqueles baseados em C ou UTF-8, são apenas os caracteres de espaço em branco acima. Em alguns locais ISO-8859-1, o espaço sem interrupção U + 00A0 é considerado em branco, incluindo Solaris, BSDs e OS X (penso incorretamente). Se você está lidando com um local desconhecido e arbitrário, ele pode incluir praticamente qualquer coisa, incluindo cartas, então boa sorte.

É possível que um único byte considerado em branco possa aparecer dentro de um caractere de vários bytes que não estivesse em branco, e você não teria como escapar disso além de colocar tudo entre aspas. Isso não é uma preocupação teórica: em um código de idioma ISO-8859-1 a partir de cima, o A0byte que é considerado um espaço em branco pode aparecer dentro de caracteres multibyte como UTF-8 codificado como "à" ( C3 A0). Para lidar com esses caracteres com segurança, você precisará citá-los "à". Esse comportamento depende da configuração do código de idioma no ambiente que está executando o script, não daquele em que você o escreveu.

Eu acho que esse comportamento é quebrado de várias maneiras, mas temos que jogar a mão que recebemos. Se você estiver trabalhando com qualquer conjunto de caracteres multibyte que não seja sincronizado automaticamente, o mais seguro seria citar tudo. Se você estiver em UTF-8 ou C, estará seguro (por enquanto).

Michael Homer
fonte
Outros espaços em branco em sua localidade precisaria escapar bem ( exceto atualmente a um multi-byte por causa de um bug )
Stéphane Chazelas
Você só precisa escapar !quando a expansão do histórico csh está ativada, normalmente não em scripts. [ ! -f a ]ou find . ! -name...estão bem. Isso é coberto pela seção de limites mais rígidos, mas talvez valha a pena mencionar explicitamente.
Stéphane Chazelas
Note que existem contextos onde outros personagens precisam citando como: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], os operadores de expressões regulares para [[ x =~ ".+[" ]]. Outras palavras-chave que {( if, while, for...) que precisam ser citado de modo que não é reconhecido como tal ...
Stéphane Chazelas
Na medida em que esses são argumentos de linha de comando, a interpretação depende do comando em questão (assim como ]), então não os estou listando. Acho que nenhuma palavra-chave precisa ser citada na posição do argumento.
Michael Homer
2
Citar builtins, traços ou% não faz nada.
Michael Homer
3

No GNU Parallel, isso é testado e usado extensivamente:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

Ele é testado em bash, dash, ash, ksh, zsh, e fish. Alguns dos caracteres não precisam ser citados em algumas (versões) dos shells, mas o acima funciona em todos os shells testados.

Se você simplesmente deseja que uma string seja citada, pode inseri-la em parallel --shellquote:

printf "&*\t*!" | parallel --shellquote
Ole Tange
fonte
Como é que eu não ouvi falar de paralelo antes ...
Tom H
@ TomH Será apreciado se você puder passar 5 minutos pensando em como poderíamos ter chegado até você.
precisa
Eu acho que é um problema de progressão. a maioria das pessoas não precisa ou entende paralelo até progredir em alguns estágios de complexidade. Quando eles já se deparam com xargs, nohup e coisas assim. Também não vejo muitas pessoas usando paralelo para resolver problemas na troca de pilhas ou quando busco soluções para resolver problemas
Tom H
1

Para uma solução de escape leve no Perl, estou seguindo o princípio de aspas simples. Uma cadeia de caracteres Bash entre aspas simples pode ter qualquer caractere, exceto a própria aspas simples.

Meu código:

my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);

while(<>) {
    if (/$bash_reserved_characters_re/) {
        my $quoted = s/'/'"'"'/gr;
        print "'$quoted'";
    } else {
        print $_;
    }
}

Exemplo de execução 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Exemplo de execução 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Exemplo de execução 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Exemplo de execução 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Exemplo de execução 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c
Jari Turkia
fonte
Sim, ponto válido que. Minha opinião é que a maioria das pessoas chegará a esta página porque tem um problema a resolver. Não porque isso faz um debate acadêmico interessante. É por isso que eu gostaria de oferecer soluções e discutir os méritos delas, mesmo estando um pouco fora de tópico.
precisa saber é o seguinte
Meu código é apenas uma implementação da resposta de Michael Homer. Não pretendia trazer mais informações do que o que ele fez.
Jari Turkia 23/01