O que o POSIX exige para os documentos citados aqui na substituição de comandos?

20

Em esta pergunta alguém relata um problema usando um documento aqui com uma palavra delimitador citado dentro $(...)substituição de comando , onde uma barra invertida \no final de uma linha dentro do gatilhos de documentos -juntando nova linha de continuação de linha , enquanto o mesmo aqui documentar fora obras de substituição de comando como esperado .

Aqui está um exemplo de documento simplificado:

cat <<'EOT'
abc ` def
ghi \
jkl
EOT

Isso inclui um backtick e uma barra invertida no final de uma linha. O delimitador é citado, portanto, nenhuma expansão ocorre dentro do corpo. Em todos os Bourne-alikes, posso encontrar isso com o conteúdo literalmente. Se eu colocar o mesmo documento dentro de uma substituição de comando da seguinte maneira:

x=$(cat <<'EOT'
abc ` def
ghi \
jkl
EOT
)
echo "$x"

então eles não se comportam mais de forma idêntica:

  • dash, ash, zsh, ksh93, BusyBox ash, mkshe SunOS 5,10 POSIX shtodos dão os conteúdos textuais do documento, como antes.
  • O Bash 3.2 fornece um erro de sintaxe para um backtick incomparável. Com backticks correspondentes, ele tenta executar o conteúdo como um comando.
  • O Bash 4.3 recolhe "ghi" e "jkl" em uma única linha, mas não apresenta erro. A --posixopção não afeta isso. Kusalananda me diz (obrigado!) Que pdkshse comporta da mesma maneira .

Na pergunta original, eu disse que isso era um bug no analisador de Bash. É isso? [Update: yes ] O texto relevante do POSIX (todos da definição da Linguagem de Comando do Shell) que posso encontrar é:

  • §2.6.3 Substituição de comando :

    Com o formulário $ (comando), todos os caracteres que seguem o parêntese aberto ao parêntese de fechamento correspondente constituem o comando. Qualquer script de shell válido pode ser usado para comando , exceto um script que consiste apenas em redirecionamentos que produz resultados não especificados.

  • §2.7.4 Aqui-Documento :

    Se qualquer parte da palavra for citada, o delimitador será formado pela remoção da cotação na palavra , e as linhas do documento aqui não serão expandidas.

  • §2.2.1 Caractere de escape (barra invertida) :

    Se uma <linha nova> segue a <barra invertida>, o shell deve interpretar isso como continuação de linha. A barra invertida e a linha nova devem ser removidas antes de dividir a entrada em tokens.

  • §2.3 Reconhecimento de token :

    Quando um token io_here for reconhecido pela gramática (consulte Shell Grammar ), uma ou mais das linhas subseqüentes imediatamente após o próximo token NEWLINE formarão o corpo de um ou mais documentos aqui e serão analisadas de acordo com as regras do Here- Documento .

    Quando não está processando um io_here , o shell deve dividir sua entrada em tokens aplicando a primeira regra aplicável abaixo ao próximo caractere em sua entrada. ...

    ...

    1. Se o caractere atual for <barra invertida>, aspas simples ou aspas duplas e não estiver entre aspas, ele afetará as aspas para os caracteres subseqüentes até o final do texto entre aspas. As regras para cotação são as descritas em Cotação . Durante o reconhecimento do token, nenhuma substituição deve ser realmente executada, e o token resultante deve conter exatamente os caracteres que aparecem na entrada (exceto para <newline> junção), sem modificação, incluindo cotações incorporadas ou anexas ou operadores de substituição, entre o e o final do texto citado.

Minha interpretação disso é que todos os caracteres depois $(até o final )compreendem o script do shell, literalmente; um documento aqui aparece, então o processamento do documento aqui ocorre em vez da tokenização comum; o documento aqui possui um delimitador entre aspas, o que significa que seu conteúdo é processado literalmente; e o personagem de escape nunca entra nele. Eu posso ver um argumento, no entanto, de que este caso simplesmente não é abordado, e ambos os comportamentos são permitidos. É possível que eu tenha pulado algum texto relevante em algum lugar também.


  • Essa situação ficou mais clara em outro lugar?
  • Em que um script portátil pode confiar (em teoria)?
  • O tratamento específico dado por qualquer uma dessas conchas (Bash 3.2 / Bash 4.3 / todos os outros) é exigido pelo padrão? Proibido? Permitido?
Michael Homer
fonte
Você pode nos mostrar como você produz sua saída no segundo caso?
Julie Pelletier
@JuliePelletier echo "$x", mas qualquer maneira de inspecionar a variável funciona. Eu editei essa linha na parte inferior.
Michael Homer
2
Parece que é uma solução fácil. Este patch parece funcionar pelo menos: ignore_quoted_newline_in_quoted_heredoc.patch
geirha
11
Eu acho que você está interpretando isso corretamente e o padrão é bem claro, pois "O shell deve expandir a substituição de comando executando o comando em um ambiente de subcama [...] e substituindo a substituição de comando pela saída padrão de the command [...] " Então ele executa o comando em um subshell e o substitui $(...)por qualquer que seja a saída ... Agora, ao executar o comando no seu exemplo em um subshell (in bash), ele gera o resultado esperado. É somente ao transformá-lo em substituição de comando que ele recolhe "ghi" e "jkl". Portanto, este é um erro imo
don_crissti
2
@geirha eu relatei um bug do Bash ; Não vou me preocupar com o pdksh, pois ele não parece ter sombra de manutenção atual.
Michael Homer

Respostas:

5

Isso foi perguntado na lista de e-mails de Bash, e o mantenedor confirmou que era um bug

Eles também mencionaram que o texto no POSIX "não é necessariamente ambíguo, mas exige uma leitura atenta.", Então pedi um esclarecimento sobre isso. Sua resposta, incluindo uma descrição do problema e interpretação do padrão, foi a seguinte:

A substituição de comando é um arenque vermelho; é relevante apenas porque apontou onde estava o erro.

O delimitador do documento aqui é citado, para que as linhas não sejam expandidas. Nesse caso, o shell lê as linhas da entrada como se estivessem entre aspas. Se uma barra invertida aparecer em um contexto em que é citada, ela não atuará como um caractere de escape (veja abaixo) e o tratamento especial da barra invertida-nova linha não ocorrerá. De fato, se qualquer parte do delimitador for citada, as linhas do documento aqui serão lidas como se fossem citadas uma única vez.

O texto no Posix 2.2.1 é escrito de forma desajeitada, mas significa que a barra invertida é tratada apenas especialmente quando não é citada. Você pode citar uma barra invertida e inibir toda a expansão apenas com aspas simples ou outra barra invertida.

A parte da leitura atenta é o texto "não expandido", que implica aspas simples. O padrão diz na 2.2 que aqui os documentos são "outra forma de citação", mas a única forma de citação na qual as palavras não são expandidas é apenas aspas simples. Portanto, é uma forma de citação que é exatamente como aspas simples, mas não aspas simples.

Kevin
fonte
@ Scott (1) Eu acredito que isso responde a todas as perguntas e nada é supérfluo. Meu comentário que inicia a resposta é sobre uma exclusão feita por um moderador que não entendeu a situação. (2) Eu não tenho reputação suficiente. (3) Eu teria apreciado um comportamento semelhante ao excluir minhas respostas, mas certamente lembrarei disso no futuro. Obrigado pelos pensamentos.
25417 Kevin
Meu argumento foi que a maior parte do seu primeiro parágrafo é uma conversa com Michael Mrozek e não uma resposta para a pergunta. Percebo que você não tem reputação suficiente para comentar em nenhuma postagem, mas acredito que você tenha o suficiente para meta e bate-papo.
25417 Scott
11
@ Scott Entendo e entendo que você está tentando simplificar a resposta, mas eu publiquei essa resposta exatamente simplificada anteriormente (apenas a citação e um link para ela) e ela foi excluída pelo moderador (sem nenhuma discussão!) E eu não há links na postagem excluída para conversar e contestar essa decisão. Eu esperava que, respondendo às suas críticas infundadas, ele sobrevivesse à exclusão, fosse aceito pelo solicitante e depois modificasse a resposta para remover o preâmbulo.
25417 Kevin