In che modo bash distingue tra espansione parentesi e raggruppamento di comandi?


48

Ho notato che {può essere utilizzato nell'espansione del controvento:

echo {1..8}

o nel raggruppamento di comandi:

{ls;echo hi}

In che modo Bash conosce la differenza?


1
Ottima domanda, +1. Sembra che potrebbe essere {interpretato come un elenco di comandi se appare all'inizio di un comando e come espansione di parentesi graffe altrimenti, ma non ne sono sicuro.
Celada,

16
{ls;echo hi}non è legale bash. È necessario uno spazio dopo la parentesi graffa di apertura e un punto e virgola prima di quello di chiusura.
PSkocik,

Risposte:


39

Un motivo semplificata è l'esistenza di un carattere: space.

Le espansioni di parentesi graffe non elaborano spazi (non quotati).

Un {...}elenco necessita di spazi (non quotati).

La risposta più dettagliata è come la shell analizza una riga di comando .


Il primo passo per analizzare (comprendere) una riga di comando è dividerlo in parti.
Queste parti (di solito chiamate parole o token) derivano dalla divisione di una riga di comando per ciascun meta-carattere dal collegamento :

  1. Suddivide il comando in token separati dal set fisso di meta-caratteri: SPAZIO, TAB, NEWLINE,;, (,), <,>, | e &. I tipi di token includono parole, parole chiave, redirector I / O e punti e virgola.

Meta-personaggi: spacetabenter;,<>|e &.

Dopo la divisione, le parole possono essere di un tipo (come compreso dalla shell):

  • Comandi pre-ordini: LC=ALL ...
  • Comando LC=ALL echo
  • argomenti LC=ALL echo "hello"
  • reindirizzamento LC=ALL echo "hello" >&2

Espansione del rinforzo

Solo se una "stringa di parentesi graffe" (senza spazi o meta-caratteri) è una singola parola (come descritto sopra) e non è quotata , è un candidato per "Espansione di parentesi graffe ". Ulteriori controlli vengono eseguiti in seguito sulla struttura interna.

Quindi, questo: si {ls,-l}qualifica come "Espansione di parentesi graffe" per diventare ls -l, come first wordo argument(in bash, zsh è diverso).

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

Ma questo non: {ls ,-l}. Bash si dividerà spacee analizzerà la riga come due parole: {lse ,-l}che attiverà un command not found(l'argomento ,-l}è perso):

 $ {ls ,-l}
 bash: {ls: command not found

La tua linea: {ls;echo hi}non diventerà una "espansione di parentesi graffe" a causa dei due meta-personaggi ;e space.

Sarà suddiviso in questi tre parti: {lsnuovo comando: echo hi}. Comprendi che ;innesca l'inizio di un nuovo comando. Il comando {lsnon verrà trovato e il comando successivo stamperà hi}:

$ {ls;echo hi}
bash: {ls: command not found
hi}

Se viene inserito dopo qualche altro comando, avvierà comunque un nuovo comando dopo il ;:

$ echo {ls;echo hi}
{ls
hi}

Elenco

Uno dei "comandi composti" è una "Lista Brace" (le mie parole): { list; }.
Come puoi vedere, è definito con spazi e una chiusura ;.
Gli spazi e ;sono necessari perché entrambi {e }sono " Parole riservate ".

E quindi, per essere riconosciuto come parole, deve essere circondato da meta-caratteri (quasi sempre:) space.

Come descritto al punto 2 della pagina collegata

  1. Controlla il primo token di ciascun comando per vedere se è ...., {, o (, quindi il comando è in realtà un comando composto.

Il tuo esempio: {ls;echo hi}non è un elenco.

Ha bisogno di una chiusura ;e uno spazio (almeno) dopo {. L'ultimo }è definito dalla chiusura ;.

Questa è una lista { ls;echo hi; }. E questo { ls;echo hi;}è anche (meno usato, ma valido) (Grazie @choroba per l'aiuto).

$ { ls;echo hi; }
A-list-of-files
hi

Ma come argomento (la shell conosce la differenza) a un comando, genera un errore:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

Ma fai attenzione a ciò che ritieni che la shell stia analizzando:

$ echo { ls;echo hi;
{ ls
hi

2
questa è davvero la risposta migliore, perché ci dai davvero come funziona il parser bash! e con una spiegazione dettagliata!
Prole ama l'

2
Non è necessario lo spazio tra ;e }. { ls;}funziona come il punto e virgola è già un meta-carattere.
Choroba,

1
@lovespring Grazie, sì, ho investito un po 'di tempo a scriverlo. Sono felice di sapere che è utile. Ancora grazie.

ottimo articolo, grazie mille per i riferimenti
Edward Torvalds,

16

Il blocco {è una parola chiave della shell, quindi deve essere separato dalla parola successiva dallo spazio, mentre nell'espansione del controvento, non dovrebbe esserci spazio (se devi rinforzare l'espansione di uno spazio, devi scappare:) echo {\ ,a}{b,c}.

È possibile utilizzare l'espansione del controvento all'inizio di un comando:

{ls,.}  # expands to "ls ."

Non è possibile utilizzarlo per espandersi in un blocco, tuttavia, poiché l'analisi dei comandi di raggruppamento avviene prima delle espansioni:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory

5

Sa controllando la sintassi della riga di comando. Allo stesso modo sa che nell'espressione echo echo, la prima eco dovrebbe essere trattata come un comando e la seconda eco come un parametro della prima eco.

In bash è molto semplice, dal momento che { cmd; }dovrebbe avere spazi e punto e virgola. Tuttavia, ad esempio in zsh non sono necessari, ma analizzando comunque il contesto della {}shell è in grado di dire cosa si dovrebbe fare con il suo contenuto.

Considera quanto segue:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

Entrambi restituiscono la data corrente, ma

echo {1..3}

ritorna 1 2 3perché la shell conosce {}un argomento per il comando echo, quindi dovrebbe essere espansa.


{seguito da spazio non quotato non avvia l'espansione del controvento in bash.
Choroba,

@choroba Sì, e non solo subito dopo {. Lo spazio non quotato non può essere da nessuna parte perché la shell divide l'intera riga di comando negli spazi.
Jimmij,

0

In primo luogo, a parentesi graffa composta deve essere una parola da sola e la prima parola della riga di comando:

echo { these braces are just words }

In secondo luogo, le singole parentesi graffe non sono speciali (come puoi vedere sopra). Anche le parentesi graffe vuote non sono speciali:

echo {} # just the token {}: familiar from the find command

Qualsiasi cosa senza virgole è anche solo se stessa

echo {abc} # just {abc}

Qui è dove inizia l'azione.

echo {a,b} # braces disappear, a b results.

Così fondamentale per l'espansione tutore di calcio in, abbiamo bisogno di una sola parola (non separati in campi spazi bianchi), all'interno del quale si verifica almeno un'istanza {...}all'interno del quale si verifica almeno una virgola.

Questa può essere la prima parola nella riga di comando, a proposito:

{ls,-l} .   # just "ls -l ."
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.