Cosa richiede POSIX per i documenti qui citati all'interno della sostituzione dei comandi?


20

In questa domanda qualcuno segnala un problema usando un documento qui con una parola delimitatore tra virgolette all'interno della $(...)sostituzione del comando , in cui una barra rovesciata \alla fine di una riga all'interno del documento innesca la continuazione della linea che unisce la nuova riga , mentre lo stesso documento qui fuori dalla sostituzione del comando funziona come previsto .

Ecco un documento di esempio semplificato:

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

Ciò include un backtick e un backslash alla fine di una riga. Il delimitatore è citato, quindi non si verificano espansioni all'interno del corpo. In tutti i Bourne-simili posso trovare questo output il contenuto alla lettera. Se inserisco lo stesso documento all'interno di una sostituzione comando come segue:

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

quindi non si comportano più in modo identico:

  • dash, ash, zsh, ksh93, BusyBox ash, mkshe SunOS 5.10 POSIX shtutti danno il contenuto testuali del documento, come prima.
  • Bash 3.2 fornisce un errore di sintassi per un backtick senza pari. Con i backtick abbinati, tenta di eseguire il contenuto come comando.
  • Bash 4.3 comprime "ghi" e "jkl" su una sola riga, ma non presenta errori. L' --posixopzione non influisce su questo. Kusalananda mi dice (grazie!) Che pdkshsi comporta allo stesso modo .

Nella domanda originale, ho detto che si trattava di un bug nel parser di Bash. È? [Aggiornamento: ] Il testo pertinente di POSIX (tutto dalla definizione del linguaggio dei comandi Shell) che posso trovare è:

  • §2.6.3 Sostituzione comando :

    Con il modulo $ (comando), tutti i caratteri che seguono la parentesi aperta alla parentesi di chiusura corrispondente costituiscono il comando. È possibile utilizzare qualsiasi script shell valido per il comando , ad eccezione di uno script costituito esclusivamente da reindirizzamenti che produce risultati non specificati.

  • §2.7.4 Here-Document :

    Se viene citata una qualsiasi parte della parola , il delimitatore deve essere formato eseguendo la rimozione della citazione sulla parola e le righe del documento qui non devono essere espanse.

  • §2.2.1 Carattere di escape (barra rovesciata) :

    Se una <newline> segue la <barra rovesciata>, la shell deve interpretarla come continuazione della linea. <slash> e <newline> devono essere rimossi prima di dividere l'input in token.

  • §2.3 Riconoscimento token :

    Quando un token io_here è stato riconosciuto dalla grammatica (vedi Shell Grammar ), una o più delle righe successive immediatamente successive al token NEWLINE successivo formano il corpo di uno o più documenti qui e devono essere analizzate secondo le regole del Here- Documento .

    Quando non sta elaborando un io_here , la shell deve suddividere il proprio input in token applicando la prima regola applicabile di seguito al carattere successivo nel suo input. ...

    ...

    1. Se il carattere corrente è <barra rovesciata>, virgoletta singola o virgoletta doppia e non viene citato, influisce sulla quotazione per i caratteri successivi fino alla fine del testo tra virgolette. Le regole per la quotazione sono descritte in Citazioni . Durante il riconoscimento del token non deve essere effettivamente eseguita alcuna sostituzione e il token risultante deve contenere esattamente i caratteri che compaiono nell'input (eccetto per il <newline> join), non modificati, comprese eventuali virgolette o operatori di sostituzione incorporati o che racchiudono, tra la e la fine del testo citato.

La mia interpretazione di questo è che tutti i personaggi che seguono $(fino al termine )comprendono lo script della shell, alla lettera; appare un documento qui, quindi l'elaborazione del documento qui avviene invece della tokenizzazione ordinaria; il documento qui ha quindi un delimitatore citato, il che significa che il suo contenuto viene elaborato alla lettera; e il personaggio di fuga non vi entra mai. Riesco a vedere una discussione, tuttavia, che questo caso semplicemente non viene affrontato ed entrambi i comportamenti sono ammessi. È possibile che io abbia saltato qualche testo rilevante anche da qualche parte.


  • Questa situazione è resa più chiara altrove?
  • Su cosa dovrebbe basarsi uno script portatile (in teoria)?
  • Il trattamento specifico fornito da una di queste conchiglie (Bash 3.2 / Bash 4.3 / chiunque altro) è obbligatorio per lo standard? Proibito? Consentito?

Puoi mostrarci come produrre il tuo output nel secondo caso?
Julie Pelletier,

@JuliePelletier echo "$x", ma qualsiasi modo di ispezionare la variabile funziona. Ho modificato quella riga in fondo.
Michael Homer,

2
Sembra che sia una soluzione semplice. Questa patch sembra funzionare almeno: ignore_quoted_newline_in_quoted_heredoc.patch
geirha

1
Penso che tu lo stia interpretando correttamente e che lo standard sia abbastanza chiaro poiché "La shell espande la sostituzione dei comandi eseguendo il comando in un ambiente subshell [...] e sostituendo la sostituzione dei comandi [...] con l'output standard di il comando [...] " Quindi esegue il comando in una subshell e lo sostituisce $(...)con qualunque cosa sia l'output ... Ora, quando si esegue il comando nell'esempio in una subshell (in bash), genera il risultato atteso. È solo quando lo trasforma in sostituzione del comando che collassa "ghi" e "jkl". Quindi questo è un bug imo
don_crissti

2
@geirha ho segnalato un bug di Bash ; Non mi preoccuperò di pdksh poiché non sembra avere nemmeno un'ombra dell'attuale manutenzione.
Michael Homer,

Risposte:


5

Questo è stato chiesto sulla mailing list di Bash e il manutentore ha confermato che si trattava di un bug

Hanno anche detto che il testo in POSIX "non è necessariamente ambiguo, ma richiede una lettura approfondita", quindi ho chiesto un chiarimento al riguardo. La loro risposta che includeva una descrizione del problema e l'interpretazione della norma era la seguente:

La sostituzione del comando è un'aringa rossa; è rilevante solo in quanto ha indicato dove si trovava il bug.

Il delimitatore al documento qui viene citato, quindi le linee non vengono espanse. In questo caso, la shell legge le righe dall'input come se fossero citate. Se una barra rovesciata appare in un contesto in cui è quotata, non funge da carattere di escape (vedi sotto) e la gestione speciale di barra rovesciata-newline non ha luogo. In effetti, se viene citata una parte del delimitatore, le righe del documento qui vengono lette come se fossero tra virgolette singole.

Il testo in Posix 2.2.1 è scritto in modo imbarazzante, ma significa che la barra rovesciata viene trattata appositamente quando non è quotata. Puoi citare una barra rovesciata e inibire tutta l'espansione solo con virgolette singole o un'altra barra rovesciata.

La parte di lettura ravvicinata è il testo "non espanso" che implica le virgolette singole. Lo standard dice in 2.2 che qui i documenti sono "un'altra forma di citazione", ma l'unica forma di citazione in cui le parole non sono affatto espanse sono le virgolette singole. Quindi è una forma di quotazione che è quasi esattamente come virgolette singole, ma non virgolette singole.


@Scott (1) Credo che questo risponda a tutte le domande e nulla è superfluo. Il mio commento che inizia la risposta riguarda una cancellazione fatta da un moderatore che ha frainteso la situazione. (2) Non ho abbastanza reputazione. (3) Avrei apprezzato un comportamento simile da parte di coloro che cancellavano le mie risposte, ma lo terrò sicuramente a mente in futuro. Grazie per i pensieri
Kevin,

Il mio punto era che la maggior parte del tuo primo paragrafo è una conversazione con Michael Mrozek e non una risposta alla domanda. Mi rendo conto che non hai abbastanza reputazione per commentare qualsiasi post, ma credo che tu abbia abbastanza per meta e chat.
Scott,

1
@Scott Capisco e apprezzo il fatto che tu stia cercando di semplificare la risposta, ma in precedenza ho pubblicato la risposta esattamente semplificata (solo la citazione e un link ad essa), ed è stato eliminato da detto moderatore (senza alcuna discussione!) E io non vedere link nel post cancellato per chattare e contestare tale decisione. Speravo che, rispondendo alle sue critiche infondate, sarebbe sopravvissuto alla cancellazione, essere accettato dall'interrogante e quindi avrei modificato la risposta per rimuovere il preambolo.
Kevin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.