virgolette doppie annidate in una sola riga altamente votata


20

Una risposta StackOverflow con> 3.5K voti presenta questo one-liner per l'assegnazione alla DIRdirectory dell'attuale script bash:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Sono perplesso dalle doppie virgolette nidificate. Per quanto ne so, i seguenti frammenti sono tra virgolette:

"$( cd "
"${BASH_SOURCE[0]}"
" && pwd )"

... e tutto il resto a destra di =(ie $( dirnamee )) non è quotato. In altre parole, suppongo che il 2o, 4o e 6o "carattere "chiudano" rispettivamente il 1o, 3o e 5o "carattere.

Capisco cosa "${BASH_SOURCE[0]}"ottengono le doppie virgolette , ma qual è lo scopo delle altre due coppie di virgolette?

Se, d'altra parte (e nonostante il punteggio più alto), lo snippet sopra riportato non è corretto, qual è il modo giusto per raggiungere il suo intento nominale?

(Per intento nominale intendo: raccogliere il valore restituito pwddopo il primo cd-ing nella directory restituita da dirname "${BASH_SOURCE[0]}", e fare il cd-ing in una sotto-shell, in modo che la $PWDshell madre rimanga invariata).


1
è a causa di una caratteristica di $ (...): $( here, it's a subshell, but you are writing code as if you were writing it on the "first level" of the shell .... ).
— Olivier Dulac,

Sono venuto qui per via dello script di installazione della finestra mobile. Per trovare il nome della distro:lsb_dist="$(. /etc/os-release && echo "$ID")"; echo "$lsb_dist"
— David Tonhofer,

Nota che non sono necessari spazi nella linea: DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"funziona anche.
— David Tonhofer,

Risposte:


12

Il tuo puzzle non ha ragione su come bash(e la shell in generale) ha analizzato l'input. Nel:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

Innanzitutto, bashanalizzare il lato destro dell'assegnazione a una stringa lunga $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )perché le virgolette doppie possono apparire tra virgolette doppie .

Successivamente, bashinizia ad analizzare la sostituzione del comando. Poiché tutti i caratteri che seguono la parentesi aperta tra parentesi chiusa vengono utilizzati per costruire il comando all'interno della sostituzione del comando, otterrai:

cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd

La shell continua ad analizzare quel comando composto, suddividendolo in due parti:

  • cd "$( dirname "${BASH_SOURCE[0]}" )"
  • pwd

Quindi applicare la stessa regola di analisi per cd "$( dirname "${BASH_SOURCE[0]}" )", ma questa volta le virgolette doppie non sono ridondanti, ma hanno senso. Impediscono la divisione del campo a seguito di $( dirname "${BASH_SOURCE[0]}" ), e anche l'espansione di ${BASH_SOURCE[0]}(Contrariamente alle virgolette più doppie esterne, non sarà necessario in RHS di assegnazione variabile per impediresplit+glob ).


Questa regola si applica alla sostituzione dei comandi in tutta la shell POSIX . Un puzzle più dettagliato che puoi leggere nella sezione Riconoscimento token delle specifiche POSIX .


21

Una volta dentro $(...), la citazione ricomincia da capo.

In altre parole, "..."e $(...)possono annidarsi l' uno nell'altro. La sostituzione del processo $(...), può contenere una o più stringhe complete tra virgolette. Inoltre, le stringhe tra virgolette doppie possono contenere una o più sostituzioni di processo complete . Ma non si intrecciano. Pertanto, una stringa tra virgolette che inizia all'interno di una sostituzione di processo non si estenderà mai al di fuori di essa o viceversa.

Quindi, considera:

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

All'interno $(...)è:

dirname "${BASH_SOURCE[0]}"

Quanto sopra ${BASH_SOURCE[0]}è tra virgolette. Eventuali virgolette, doppie o singole, al di fuori del $(...)sono irrilevanti quando si determina che ${BASH_SOURCE[0]}sono tra virgolette doppie.

L'esterno $(...)contiene:

cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd

Qui, l'espressione $( dirname "${BASH_SOURCE[0]}" )è racchiusa tra virgolette. Il fatto che ci siano virgolette fuori dall'esterno $(...)è irrilevante quando si considera ciò che è al suo interno. Anche il fatto che ci siano virgolette all'interno $(...)è irrilevante.

Ecco come corrispondono le virgolette doppie:

inserisci qui la descrizione dell'immagine


Cosa intendi irrelevant? Ad eccezione della maggior parte delle parentesi esterne, tutte le altre hanno un loro significato.
— cuonglm

4
Ed è per questo che non dovresti usare i backtick: le regole di quotazione sono estremamente strane e non intuitive.
— Wildcard il

La frase "è $(...)più stretta di "..."" non ha senso. Non sono operatori di infissione e non esiste una gerarchia tra di loro (se ci fosse, ciò significherebbe che entrambe le virgolette non possono essere racchiuse tra parentesi o tra parentesi, ma non è così). Il termine tecnico è questo $(…)e "…"nidificano.
— Gilles 'SO- smetti di essere malvagio' il

@Gilles Molto bene. Ho appena riformulato la speranza di catturare meglio il concetto.
— Giovanni 1024
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.