O que o POSIX requer para documentos aqui citados dentro da substituição de comandos?

17

Em esta pergunta alguém relata um problema usando um aqui documento com uma palavra delimitadora entre aspas $(...) substituição de comandos , onde uma barra invertida \ no final de uma linha dentro do documento dispara continuação da linha de junção , enquanto a mesma substituição de comando fora do documento aqui funciona como esperado.

Aqui está um documento de exemplo 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 eu posso encontrar isso produz o conteúdo textualmente. Se eu colocar o mesmo documento dentro de uma substituição de comando da seguinte forma:

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

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

  • dash , ash , zsh , ksh93 , BusyBox ash , mksh e SunOS 5.10 POSIX sh todos fornecem o conteúdo textual do documento, como antes.
  • O Bash 3.2 apresenta um erro de sintaxe para um backtick sem correspondência. Com os 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 tem erro. A --posix option não afeta isso. Kusalananda me diz (obrigado! ) que pdksh se comporta da mesma maneira .

Na pergunta original, eu disse que isso era um bug no analisador do Bash. É isso? [Atualização: sim ] O texto relevante de POSIX (todos do Definição de linguagem de comando do shell) que eu posso encontrar é:

  • §2.6.3 Substituição de comando :

    With the $(command) form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.

  • §2.7.4 Documento aqui :

    If any part of word is quoted, the delimiter shall be formed by performing quote removal on word, and the here-document lines shall not be expanded.

  • §2.1.1 Caractere de escape (barra invertida) :

    If a <newline> follows the <backslash>, the shell shall interpret this as line continuation. The <backslash> and <newline> shall be removed before splitting the input into tokens.

  • §2.3 Reconhecimento de Token :

    When an io_here token has been recognized by the grammar (see Shell Grammar), one or more of the subsequent lines immediately following the next NEWLINE token form the body of one or more here-documents and shall be parsed according to the rules of Here-Document.

    When it is not processing an io_here, the shell shall break its input into tokens by applying the first applicable rule below to the next character in its input. ...

    ...

    1. If the current character is <backslash>, single-quote, or double-quote and it is not quoted, it shall affect quoting for subsequent characters up to the end of the quoted text. The rules for quoting are as described in Quoting . During token recognition no substitutions shall be actually performed, and the result token shall contain exactly the characters that appear in the input (except for <newline> joining), unmodified, including any embedded or enclosing quotes or substitution operators, between the and the end of the quoted text.

A minha interpretação disto é que todos os caracteres após $( até o final de ) compreendem o script de shell, literalmente; um documento aqui aparece, portanto ocorre o processamento do documento aqui, em vez da tokenização comum; o documento aqui então tem um delimitador entre aspas, o que significa que seu conteúdo é processado textualmente; e o personagem de escape nunca entra nele. Eu posso ver um argumento, entretanto, que este caso simplesmente não é endereçado, e ambos os comportamentos são permissíveis. É possível que eu tenha pulado algum texto relevante em algum lugar também.

  • Esta situação é esclarecida em outro lugar?
  • O que um script portátil pode confiar (em teoria)?
  • O tratamento específico dado por algum desses shells (Bash 3.2 / Bash 4.3 / todos os outros) é determinado pela norma? Proibido? Permitido?
por Michael Homer 29.01.2017 / 06:20

1 resposta

1

@MichaelMrozek A resposta anterior não foi vinculada a um encadeamento iniciado pelo consulente. O consulente fez uma pergunta na lista de discussão e a resposta foi que era um erro. O consulente não acompanhou a discussão da lista de discussão. Eu publiquei um novo tópico da lista de discussão na noite passada para fazer o acompanhamento com o mantenedor do Bash, e ele respondeu exatamente com uma resposta a essa pergunta. Como exatamente você acredita que a resposta a seguir não responde à pergunta?

The command substitution is a red herring; it's relevant only in that it pointed out where the bug was.

The delimiter to the here-document is quoted, so the lines are not expanded. In this case, the shell reads lines from the input as if they were quoted. If a backslash appears in a context where it is quoted, it does not act as an escape character (see below), and the special handling of backslash-newline does not take place. In fact, if any part of the delimiter is quoted, the here-document lines are read as if single-quoted.

The text in Posix 2.2.1 is written awkwardly, but means that the backslash is only treated specially when it's not quoted. You can quote a backslash and inhibit all all expansion only with single quotes or another backslash.

The close reading part is the "not expanded" text implying the single quotes. The standard says in 2.2 that here documents are "another form of quoting," but the only form of quoting in which words are not expanded at all is single quotes. So it's a form of quoting that is just about exactly like single quotes, but not single quotes.

link

    
por 24.02.2017 / 22:14