O conselho antigo costumava ser o de citar duas vezes qualquer expressão que envolva a $VARIABLE
, pelo menos se alguém quisesse que ela fosse interpretada pelo shell como um único item; caso contrário, qualquer espaço no conteúdo de $VARIABLE
isso jogaria fora o shell.
Entendo, no entanto, que em versões mais recentes de shells, as aspas duplas nem sempre são necessárias (pelo menos para o objetivo descrito acima). Por exemplo, em bash
:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
Por zsh
outro lado, os mesmos três comandos são bem-sucedidos. Portanto, com base nesse experimento, parece que, em bash
, é possível omitir aspas duplas dentro [[ ... ]]
, mas não dentro [ ... ]
nem em argumentos de linha de comando, enquanto que, em zsh
, as aspas duplas podem ser omitidas em todos esses casos.
Mas inferir regras gerais a partir de exemplos anedóticos, como o acima, é uma proposição arriscada. Seria bom ver um resumo de quando as aspas duplas são necessárias. Estou principalmente interessado em zsh
, bash
e /bin/sh
.
SH_WORD_SPLIT
opção.Respostas:
Primeiro, separe o zsh do resto. Não se trata de conchas antigas e modernas: o zsh se comporta de maneira diferente. Os designers do zsh decidiram torná-lo incompatível com os shells tradicionais (Bourne, ksh, bash), mas mais fáceis de usar.
Segundo, é muito mais fácil usar aspas duplas o tempo todo do que lembrar quando elas são necessárias. Eles são necessários na maioria das vezes, portanto, você precisará aprender quando não forem necessários, não quando forem necessários.
Em poucas palavras, aspas duplas são necessárias sempre que uma lista de palavras ou um padrão é esperado . Eles são opcionais em contextos em que uma sequência bruta é esperada pelo analisador.
O que acontece sem aspas
Observe que, sem aspas duplas, duas coisas acontecem.
${foo}
, ou a saída do comando para uma substituição de comando como$(foo)
) é dividido em palavras onde quer que haja espaço em branco.Mais precisamente, o resultado da expansão é dividido em cada caractere que aparece no valor da
IFS
variável (caractere separador). Se uma sequência de caracteres separadores contiver espaços em branco (espaço, tabulação ou nova linha), o espaço em branco será contabilizado como um único caractere; separadores à esquerda, à esquerda ou repetidos em branco não levam a campos vazios. Por exemplo, withIFS=" :"
,:one::two : three: :four
produz campos vazios antesone
, entreone
etwo
, e (um único) entrethree
efour
.\[*?
. Se esse padrão corresponder a um ou mais nomes de arquivos, o padrão será substituído pela lista de nomes de arquivos correspondentes.Uma expansão de variável não citada
$foo
é conhecida coloquialmente como "operador split + glob", em contraste com o"$foo"
qual apenas assume o valor da variávelfoo
. O mesmo vale para substituição de comando:"$(foo)"
é uma substituição de comando,$(foo)
é uma substituição de comando seguida por split + glob.Onde você pode omitir as aspas duplas
Aqui estão todos os casos em que posso pensar em um shell no estilo Bourne, no qual você pode escrever uma substituição de variável ou comando sem aspas duplas, e o valor é interpretado literalmente.
No lado direito de uma tarefa.
Observe que você precisa de aspas duplas depois
export
, porque é um builtin comum, não uma palavra-chave. Isso é verdade apenas em alguns shells, como dash, zsh (emulação sh), yash ou chique; o bash e o ksh tratamexport
especialmente.Em uma
case
declaração.Observe que você precisa de aspas duplas em um padrão de caso. A divisão de palavras não ocorre em um padrão de maiúsculas e minúsculas, mas uma variável entre aspas é interpretada como um padrão, enquanto uma variável entre aspas é interpretada como uma sequência literal.
Dentro de colchetes duplos. Parênteses duplos são sintaxe especial do shell.
Exceto que você precisa de aspas duplas onde é esperado um padrão ou expressão regular: no lado direito de
=
ou==
ou!=
ou=~
.Você precisa de aspas duplas, como de costume, entre colchetes,
[ … ]
porque elas são sintaxe comum do shell (é um comando que é chamado[
). Consulte Parênteses simples ou duplosEm um redirecionamento em shells POSIX não interativos (não
bash
, nemksh88
).Algumas shells, quando interativas, tratam o valor da variável como um padrão curinga. O POSIX proíbe esse comportamento em shells não interativos, mas alguns shells, incluindo o bash (exceto no modo POSIX) e o ksh88 (inclusive quando encontrado como o (supostamente) POSIX
sh
de alguns Unices comerciais como o Solaris) ainda o fazem lá (bash
também tentam dividir e o redirecionamento falhará, a menos que a divisão + oclusão resulte em exatamente uma palavra), e é por isso que é melhor citar destinos de redirecionamentos em umsh
script, caso você queira convertê-lo em umbash
script algum dia ou executá-lo em um sistema ondesh
está não compatível nesse ponto ou pode ser proveniente de shells interativos.Dentro de uma expressão aritmética. De fato, você precisa deixar as aspas para que uma variável seja analisada como uma expressão aritmética.
No entanto, você precisa das aspas em torno da expansão aritmética, pois elas estão sujeitas à divisão de palavras na maioria dos shells, conforme o POSIX exige (!?).
Em uma matriz associativa subscrita.
Uma variável não citada e substituição de comando podem ser úteis em algumas circunstâncias raras:
$IFS
não foi modificado e você deseja dividi-lo em caracteres de espaço em branco.set -f
, definaIFS
o caractere separador (ou deixe em branco para dividir no espaço em branco) e faça a expansão.Zsh
No zsh, você pode omitir as aspas duplas na maioria das vezes, com algumas exceções.
$var
nunca se expande para várias palavras, no entanto, se expande para a lista vazia (em oposição a uma lista que contém uma única palavra vazia) se o valor devar
for a sequência vazia. Contraste:Da mesma forma, se
"${array[@]}"
expande para todos os elementos da matriz, enquanto se$array
expande apenas para os elementos não vazios.A
@
bandeira de expansão de parâmetros, por vezes, requer aspas em torno toda a substituição:"${(@)foo}"
.A substituição de comando sofre divisão de campo se não estiver entre aspas:
echo $(echo 'a'; echo '*')
imprimea *
(com um único espaço) enquantoecho "$(echo 'a'; echo '*')"
imprime a sequência de duas linhas não modificada. Use"$(somecommand)"
para obter a saída do comando em uma única palavra, sem novas linhas finais. Use"${$(somecommand; echo _)%?}"
para obter a saída exata do comando, incluindo novas linhas finais. Use"${(@f)$(somecommand)}"
para obter uma matriz de linhas da saída do comando.fonte
echo "$(("$expr"))"
man bash
diz: A expressão é tratada como se estivesse entre aspas duplas, mas uma aspas dupla entre parênteses não é tratada especialmente.echo
. Pode valer a pena tentar tornar a linguagem ainda mais explícito ( "quando uma corda crua é esperado pelo analisador", talvez?)