Como as aspas duplas no bash correspondem (emparelhadas)?


15

Eu 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

 * 

Então, obviamente, no primeiro caso, *é globbed, o que significa que a primeira aspas vai com a segunda para formar um par, e a terceira e a quarta formam outro par.

No segundo caso *, ele não é globulado e há exatamente dois espaços extras na saída, um antes do asterisco e um depois, o que significa que a segunda aspas vai com o terceiro e a primeira com o quarto.

Além da $()construção, existem outros casos em que as aspas não coincidem com a próxima, mas estão aninhadas? Esse comportamento está bem documentado e, em caso afirmativo, onde posso encontrar o documento correspondente?



Mais uma vez, o destaque da sintaxe no UL SE é errado e enganoso, embora pretenda que JS seja o culpado.
Weijun Zhou

Respostas:


14

Qualquer uma das construções de aninhamento que podem ser interpoladas dentro de cadeias de caracteres pode ter outras cadeias 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. Todos, exceto um daqueles, começam com a $. Todos eles estão documentados em uma combinação do manual do Bash e da especificação da linguagem de comandos do shell POSIX.

Existem alguns casos dessas construções:

  • Substituição de comando com$( ... ) , como você encontrou. POSIX especifica este comportamento :

    Com o $(command)formulário, todos os caracteres que seguem o parêntese aberto ao parêntese de fechamento correspondente constituem o comando. Qualquer script shell válido pode ser usado para comando ...

    As cotações fazem parte de scripts de shell válidos, portanto, são permitidas com seu significado normal.

  • Substituição de comando usando `também.
  • O elemento "word" das instâncias avançadas de substituição de parâmetros, como${parameter:-word} . A definição de "palavra" é :

    Uma sequência de caracteres tratados como uma unidade pelo shell

    - que inclui texto citado e até 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}também funcione.

  • Expansão aritmética com $(( ... )), embora seja amplamente inútil lá (mas você também pode aninhar substituição de comando ou expansões variáveis, e depois ter aspas úteis dentro delas). O POSIX afirma que :

    A expressão deve ser tratada como se estivesse entre aspas duplas, exceto que as aspas duplas dentro da expressão não são tratadas especialmente. O shell deve expandir todos os tokens na expressão para expansão de parâmetro, substituição de comando e remoção de cotação.

    portanto, esse comportamento é explicitamente necessário. Isso significa echo "abc $((4 "*" 5))"aritmética, em vez de globbing.

    Observe 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 ser citada ou não. Este formulário não está mais documentado e não deve ser usado de qualquer maneira.

  • Tradução específica de localidade com$"..." , que na verdade usa o "como elemento principal. $"é tratado como uma única unidade.

Há um outro caso de aninhamento que você não pode esperar, sem envolver cotações, que é a expansão entre chaves : {a,b{c,d},e}expande para "a bc bd e". ${x:-a{b,c}d}faz não ninho, no entanto; é tratado como uma substituição de parâmetro, fornecendo " a{b,c", seguido de " d}". Isso também está documentado :

Quando chaves são usadas, a chave final correspondente é o primeiro '}' não escapado por uma barra invertida ou dentro de uma cadeia de caracteres citada, e não dentro de uma expansão aritmética incorporada, substituição de comando ou expansão de parâmetro.


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 solicita ao analisador que consuma o que pode do corpo como se fosse um novo programa e verifica se o marcador final esperado (um sem escape )ou ))ou }) aparece quando o sub-analisador é executado das coisas que pode consumir.

Se você pensar no funcionamento de um analisador de descida recursiva , isso é apenas uma recursão simples para o caso base. Na verdade, é mais fácil de fazer do que o contrário, quando você tem interpolação de strings. Independentemente da técnica de análise subjacente, as conchas que suportam essas construções fornecem o mesmo resultado.

Você pode aninhar a citação o quanto quiser por meio dessas construções e funcionará conforme o esperado. 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.


Obrigado. Em "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", por que a segunda citação dupla não é o fim da primeira citação dupla (como mostra a sintaxe destacada em sua resposta), mas o início de uma cadeia de caracteres dentro $(...)? É porque o analisador do bash é de cima para baixo, em vez de de baixo para cima?
StackExchange for All

2
Há muita variação no manuseio de "${var-"foo"}"( echo "${-+"*"}"é o mesmo que echo *no shell Bourne ou Korn, por exemplo) e o comportamento será tornado claramente não especificado na próxima versão do padrão . Veja também a discussão em mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Talvez examinar os dois exemplos com printf(em vez de echo) ajude:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Imprime (echo (a primeira palavra, incluindo um espaço à direita), alguns arquivos e a palavra final ).
O parêntese é apenas parte da cadeia de caracteres citada (echo .
O asterisco (agora sem aspas, pois as duas aspas duplas são emparelhadas) é expandido como um globo para uma lista de arquivos correspondentes.
E então, o parêntese de fechamento.

No entanto, seu segundo comando funciona da seguinte maneira:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

O $inicia uma substituição de comando. Isso começa de novo a citação.
O asterisco é citado " * "e é isso que o comando (aqui é um comando e não uma string entre aspas) echogera. Finalmente, printfre-formata *e imprime como < * >.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.