Come vengono abbinate le doppie virgolette in bash (accoppiate)?


15

Sto usando GNU bash 4.3.48. Considera i seguenti due comandi che differiscono solo per un singolo simbolo di dollaro.

Comando 1:

echo "(echo " * ")"

Comando 2:

echo "$(echo " * ")"

Il loro output è, rispettivamente

(echo  test.txt ppcg.sh )

e

 * 

Quindi, ovviamente, nel primo caso *è globbed, il che significa che la prima virgoletta va con la seconda per formare una coppia, e la terza e la quarta formano un'altra coppia.

Nel secondo caso il *non è globbed e ci sono esattamente due spazi extra nell'output, uno prima dell'asterisco e uno dopo, il che significa che la seconda virgoletta va con la terza e la prima con la quarta.

Esistono altri casi oltre alla $()costruzione in cui le virgolette non corrispondono a quella successiva, ma sono invece nidificate? Questo comportamento è ben documentato e, in caso affermativo, dove posso trovare il documento corrispondente?



Ancora una volta, l'evidenziazione della sintassi in UL SE è sbagliata e fuorviante, anche se è giusto incolpare JS.
Weijun Zhou,

Risposte:


14

Tutti i costrutti di annidamento che possono essere interpolati all'interno di stringhe possono avere ulteriori stringhe al loro interno: vengono analizzati come un nuovo script, fino al marcatore di chiusura e possono anche essere nidificati a più livelli di profondità. Tutti tranne uno di questi inizia con a $. Tutti sono documentati in una combinazione del manuale Bash e delle specifiche del linguaggio dei comandi della shell POSIX.

Ci sono alcuni casi di questi costrutti:

  • Sostituisci i comandi con$( ... ) , come hai trovato. POSIX specifica questo comportamento :

    Con il $(command)modulo, tutti i caratteri che seguono la parentesi aperta alla parentesi di chiusura corrispondente costituiscono il comando. Qualsiasi script shell valido può essere utilizzato per il comando ...

    Le citazioni fanno parte di script shell validi, quindi sono consentite con il loro significato normale.

  • Usando `anche la sostituzione dei comandi .
  • L'elemento "word" di istanze avanzate di sostituzione dei parametri come${parameter:-word} . La definizione di "parola" è :

    Una sequenza di personaggi trattati come un'unità dalla shell

    - che include testo tra virgolette e persino virgolette miste a"b"c'd'e- sebbene il comportamento reale delle espansioni sia un po 'più liberale di quello, e per esempio ${x:-hello world}funziona anche.

  • Espansione aritmetica con $(( ... )), sebbene sia in gran parte inutile lì (ma puoi annidare anche la sostituzione dei comandi o espansioni variabili, e quindi avere virgolette all'interno di quelle). POSIX afferma che :

    L'espressione deve essere trattata come se fosse tra virgolette doppie, tranne per il fatto che una virgoletta doppia all'interno dell'espressione non è trattata in modo speciale. La shell espande tutti i token nell'espressione per l'espansione dei parametri, la sostituzione dei comandi e la rimozione delle quotazioni.

    quindi questo comportamento è esplicitamente richiesto. Ciò significa echo "abc $((4 "*" 5))"che è aritmetica, piuttosto che sconvolgente.

    Nota che l' $[ ... ]espansione aritmetica vecchio stile non viene trattata allo stesso modo: le virgolette saranno un errore se compaiono, indipendentemente dal fatto che l'espansione sia quotata o meno. Questo modulo non è più documentato e non deve essere utilizzato comunque.

  • Traduzione specifica delle impostazioni locali con$"..." , che attualmente utilizza l' "elemento come elemento principale. $"viene trattato come una singola unità.

C'è un altro caso di nidificazione che potresti non aspettarti, senza coinvolgere le virgolette, che è con l' espansione del controvento : si {a,b{c,d},e}espande in "a bc bd e". ${x:-a{b,c}d}fa non nido, tuttavia; viene trattato come una sostituzione di parametro che fornisce " a{b,c", seguito da " d}". Questo è anche documentato :

Quando vengono utilizzate le parentesi graffe, la parentesi finale corrispondente è il primo '}' non sfuggito a una barra rovesciata o all'interno di una stringa tra virgolette e non all'interno di un'espansione aritmetica incorporata, sostituzione di comando o espansione dei parametri.


Come regola generale, tutti i costrutti delimitati analizzano i loro corpi indipendentemente dal contesto circostante (e le eccezioni sono trattate come bug ). In sostanza, vedendo $(il codice di sostituzione dei comandi chiede al parser di consumare ciò che può dal corpo come se fosse un nuovo programma, quindi verifica che l'indicatore di terminazione atteso (un escape )o ))o }) appaia una volta eseguito il parser secondario dalle cose che può consumare.

Se si pensa al funzionamento di un parser a discesa ricorsiva , è solo una semplice ricorsione al caso base. In realtà è più facile da fare rispetto al contrario, una volta ottenuta l'interpolazione delle stringhe. Indipendentemente dalla tecnica di analisi sottostante, i gusci che supportano questi costrutti danno lo stesso risultato.

Puoi annidare citando quanto più ti piace attraverso questi costrutti e funzionerà come previsto. In nessun posto si confonderà vedendo una citazione nel mezzo; invece, sarà l'inizio di una nuova stringa tra virgolette nel contesto interno.


Grazie. In "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", perché la seconda virgoletta doppia non è la fine della prima virgoletta doppia (come mostrato dall'evidenziazione della sintassi nella risposta), ma l'inizio di una stringa all'interno $(...)? È perché il parser di bash è top-down, anziché bottom-up?
StackExchange per tutto il

2
Esistono molte variazioni nella gestione di "${var-"foo"}"( echo "${-+"*"}"è la stessa echo *della shell Bourne o Korn per esempio) e il comportamento sarà reso chiaramente non specificato nella prossima versione dello standard . Vedi anche discussione su mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Forse guardare i due esempi con printf(anziché echo) aiuterà:

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

Stampa (echo (la prima parola, incluso uno spazio finale), alcuni file e la parola di chiusura ).
La parentesi è solo una parte della stringa tra virgolette (echo .
L'asterisco (ora non quotato, poiché le due virgolette doppie sono accoppiate) viene espanso come glob in un elenco di file corrispondenti.
E poi, la parentesi di chiusura.

Tuttavia, il tuo secondo comando funziona come segue:

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

Il $avvia una sostituzione di comando. Questo inizia di recente la quotazione.
L'asterisco è citato " * "e questo è ciò che il comando (qui è un comando e non una stringa tra virgolette) echogenera. Infine, printfriformatta *e lo stampa come < * >.

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.