estou usando GNU bash
4.3.48
. Considere os dois comandos a seguir que diferem apenas por um único cifrão.
Comando 1:
echo "(echo " * ")"
Comando 2:
echo "$(echo " * ")"
A saída deles são, respectivamente
(echo test.txt ppcg.sh )
e
*
Portanto, obviamente, no primeiro caso, o *
é globbed, o que significa que a primeira aspa acompanha a segunda para formar um par, e a terceira e a quarta formam outro par.
No segundo caso, o *
não é globbed e há exatamente dois espaços extras na saída, um antes do asterisco e outro depois, o que significa que a segunda aspa acompanha a terceira e a primeira acompanha a quarta.
Existem outros casos além da $()
construção em que as aspas não correspondem à próxima, mas estão aninhadas? Esse comportamento está bem documentado e, em caso afirmativo, onde posso encontrar o documento correspondente?
Qualquer uma das construções de aninhamento que podem ser interpoladas dentro de strings pode ter outras strings dentro delas: elas são analisadas como um novo script, até o marcador de fechamento e podem até ser aninhadas em vários níveis de profundidade. Todas as barras começam com um
$
. Todos eles são documentados em uma combinação do manual Bash e especificação de linguagem de comando shell POSIX.Existem alguns casos dessas construções:
Substituição de comando por
$( ... )
, como você encontrou. POSIX especifica este comportamento :As aspas fazem parte de scripts de shell válidos, portanto, são permitidas com seu significado normal.
`
, também.O elemento "palavra" de instâncias avançadas de substituição de parâmetros, como
${parameter:-word}
. A definição de "palavra" é :- que inclui texto citado e até mesmo citações mistas
a"b"c'd'e
- embora o comportamento real das expansões seja um pouco mais liberal do que isso e, por exemplo,${x:-hello world}
funcione também.Expansão aritmética com
$(( ... ))
, embora seja amplamente inútil lá (mas você também pode aninhar a substituição de comandos ou expansões de variáveis e, em seguida, colocar aspas utilmente dentro delas). O POSIX afirma que :portanto, esse comportamento é explicitamente necessário. Isso significa que
echo "abc $((4 "*" 5))"
faz aritmética, em vez de globbing.$[ ... ]
Observe, porém, que a expansão aritmética de estilo antigo não é tratada da mesma maneira: as aspas serão um erro se aparecerem, independentemente de a expansão estar entre aspas ou não. Este formulário não está mais documentado e não deve ser usado de qualquer maneira.$"..."
, que na verdade usa o"
como um elemento principal.$"
é tratado como uma única unidade.Há mais um caso de aninhamento que você pode não esperar, não envolvendo aspas, que é com expansão de chaves :
{a,b{c,d},e}
expande para "a bc bd e".${x:-a{b,c}d}
não aninha, no entanto; é tratado como uma substituição de parâmetro dando "a{b,c
", seguido de "d}
". Isso também está documentado :Como regra geral, todas as construções delimitadas analisam seus corpos independentemente do contexto circundante (e as exceções são tratadas como bugs ). Em essência, ao ver
$(
o código de substituição de comando, apenas pede ao analisador para consumir o que puder do corpo como se fosse um novo programa e, em seguida, verifica se o marcador de término esperado (um sem escape)
ou))
ou}
) aparece quando o subanalisador é executado das coisas que pode consumir.Se você pensar sobre o funcionamento de um analisador descendente recursivo , isso é apenas uma recursão simples para o caso base. Na verdade, é mais fácil fazer do que o contrário, uma vez que você tenha interpolação de strings. Independentemente da técnica de análise subjacente, os shells que suportam essas construções fornecem o mesmo resultado.
Você pode aninhar as citações tão profundamente quanto quiser por meio dessas construções e funcionará conforme o esperado. Em nenhum lugar ficará confuso ao ver uma citação no meio; em vez disso, será o início de uma nova string entre aspas no contexto interior.
Talvez olhar para os dois exemplos com
printf
(em vez deecho
) ajude:Ele imprime
(echo
(a primeira palavra, incluindo um espaço à direita), alguns arquivos e a palavra final)
.O parêntese é apenas parte da string entre aspas
(echo
.O asterisco (agora sem aspas, pois as duas aspas duplas estão emparelhadas) é expandido como um glob para uma lista de arquivos correspondentes.
E então, o parêntese de fechamento.
No entanto, seu segundo comando funciona da seguinte maneira:
O
$
inicia uma substituição de comando. Isso inicia uma nova citação.O asterisco é citado
" * "
e é isso que o comando (aqui é um comando e não uma string entre aspas)echo
gera. Finalmente,printf
reformata*
e imprime como< * >
.