Perché `[` una shell integrata e `[[` una parola chiave shell?


64

Per quanto ne so, [[è una versione migliorata di [, ma sono confuso quando vedo [[come parola chiave e [viene mostrato come incorporato.

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

Dice TLDP

Un builtin può essere sinonimo di un comando di sistema con lo stesso nome, ma Bash lo reimplementa internamente. Ad esempio, il comando Bash echo non è lo stesso di / bin / echo, sebbene il loro comportamento sia quasi identico.

e

Una parola chiave è una parola, un token o un operatore riservato. Le parole chiave hanno un significato speciale per la shell, e in effetti sono i mattoni della sintassi della shell. Ad esempio, per, while, do e! sono parole chiave. Simile a un builtin, una parola chiave è codificata in Bash, ma a differenza di un builtin, una parola chiave non è di per sé un comando, ma una subunità di un costrutto di comando. [2]

Questo non dovrebbe rendere entrambi [e [[una parola chiave? C'è qualcosa che mi manca qui? Inoltre, questo link ri-afferma che sia [e [[dovrebbe appartenere allo stesso genere.



9
/ bin / [esiste sulla mia macchina.
Joshua,

2
Come semplice dimostrazione di una differenza tra i due: if "[" $x -eq 3 ]funziona come previsto (perché Bash cerca il comando chiamato [, e questo esiste), ma if "[[" $x -eq 3 ]]non non funziona (perché ancora una volta Bash cerca un comando del nome appropriato, ma non c'è [[comando).
Kyle Strand,

1
@Joshua Anche così /usr/bin/echo, ma ciò non significa che non sia incorporato .
Jonathon Reinhart,

Tutti i bulitin che esistono anche esternamente analizzano gli argomenti se non fossero builtin.
Giosuè,

Risposte:


80

La differenza tra [ed [[è abbastanza fondamentale.

  • [è un comando. I suoi argomenti vengono elaborati esattamente come gli altri argomenti dei comandi. Ad esempio, considera:

    [ -z $name ]

    Il guscio si espanderà $nameed eseguire sia la suddivisione e la generazione nome sul risultato, proprio come farebbe per qualsiasi altro comando.

    Ad esempio, non riuscirà:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments

    Per farlo funzionare correttamente, sono necessarie le virgolette:

    $ [ -n "$name" ] && echo not empty
    not empty
  • [[è una parola chiave shell e i suoi argomenti vengono elaborati secondo regole speciali. Ad esempio, considera:

    [[ -z $name ]]

    La shell si espanderà $namema, a differenza di qualsiasi altro comando, non eseguirà né la suddivisione delle parole né la generazione del nome del file sul risultato. Ad esempio, quanto segue avrà successo nonostante gli spazi incorporati in name:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty

Sommario

[ è un comando ed è soggetto alle stesse regole di tutti gli altri comandi eseguiti dalla shell.

Perché [[è una parola chiave, non un comando, tuttavia la shell la tratta in modo speciale e opera secondo regole molto diverse.


+1. Grazie. Potresti dare la fonte (riferimento) per sotto quali regole operano un comando e una parola chiave?
Tim

@Tim Le regole in base alle quali operano i comandi sono descritte in dettaglio man bash. Vedi, in particolare, le sezioni intitolate "SEMPLICE ESPANSIONE DEI COMANDI" e "ESECUZIONE DEI COMANDI". In aggiunta a [[, altri bash parole chiave includono if, then, while, e 'caso'. Non ci sono regole generali per le parole chiave: ogni parola chiave è un caso speciale. man bash include dettagli per ciascuno.
Giovanni 1024

62

In V7 Unix - dove la shell Bourne ha fatto il suo debutto - è [stato chiamato test, ed esisteva solo come /bin/test. Quindi, codice che scriveresti oggi come:

if [ "$foo" = "bar" ] ; then ...

avresti scritto invece come

if test "$foo" = "bar" ; then ...

Questa seconda notazione esiste ancora e trovo che sia più chiaro cosa sta succedendo: stai chiamando un comando chiamato test, che valuta i suoi argomenti e restituisce un codice di stato di uscita che ifutilizza per decidere cosa fare dopo. Tale comando può essere incorporato nella shell oppure può essere un programma esterno .¹

[in alternativa è testvenuto dopo.² Potrebbe essere un sinonimo incorporato test, ma è anche fornito come /bin/[sui sistemi moderni per shell che non lo hanno come builtin.

[e testpuò essere implementato utilizzando lo stesso codice. Questo è il caso di /bin/[e /bin/testsu OS X, dove si tratta di collegamenti diretti allo stesso eseguibile.³ Di conseguenza, l'implementazione ignora completamente il trascinamento ]: non lo richiede se lo chiami come /bin/[, e non si lamenta se si fa fornire a /bin/test.⁴

Nessuna di quella storia influenza [[, perché non è mai stato chiamato un programma primordiale [[. Esiste puramente all'interno di quelle shell che lo implementano come estensione della shell POSIX .

Parte della distinzione tra "builtin" e "keyword" è dovuta a questa storia. Riflette anche il fatto che le regole di sintassi per l'analisi delle [[espressioni sono diverse, come sottolineato nella risposta di John1024.


Note:

  1. Quando lo si osserva in questo modo, viene chiarito il motivo per cui è necessario inserire spazi [negli script shell, a differenza del modo in cui parentesi e parentesi funzionano nella maggior parte degli altri linguaggi di programmazione. Se il parser di comando della shell lo consentisse if["$x"..., dovrebbe anche consentireiftest"$x"...

  2. È successo intorno al 1980. /bin/[Non esiste nella mia copia di Ancient Unix V7 del 1979, né lo man testdocumenta come alias. Nella corrispondente voce della pagina man che ho in una copia pre-release del manuale di System III del 1980, è elencato.

  3. ls -i /bin/[ /bin/test

  4. Ma non contare su questo comportamento. La versione di Bash built-in del [richiede la chiusura ], e la sua built-in testdi attuazione si lamenterà se non fornirlo.

  5. La distinzione tra comando incorporato e comando esterno può anche essere importante per un'altra ragione: le due implementazioni possono comportarsi diversamente. Questo è il caso di echomolti sistemi. Poiché esiste una sola implementazione, non è necessario fare tale distinzione per una parola chiave.


Grazie @Warren Young, ma perché builtine la keyworddistinzione tra [e [[quando entrambe forniscono la stessa funzionalità (tranne il fatto che [[viene fornito con più funzionalità di [)?
Sree,

2
@sree [[essendo una parola chiave consente a bash di fare cose con le quali non è possibile [, ad esempio la citazione non è necessaria per molto tempo, perché la shell è consapevole che è una variabile. Vale a dire, l'elaborazione della riga di comando è influenzata quando viene utilizzata una parola chiave, ma non quando viene utilizzato un built-in, ciò accade molto più tardi.
Muru,

1
Si noti che il codice per la shell V7 Bourne mostra un [builtin, ma il codice è commentato.
Stéphane Chazelas,

cdè un built-in, ma non oscura nulla ( cdnon può essere implementato come programma esterno).
Paŭlo Ebermann,

2
Questo è un eccellente riassunto storico, ma tralascia il dettaglio cruciale (IMO), sottolineato da Muru sopra e da John 1024 nella loro risposta, che la creazione di [[una parola chiave consente alla shell di utilizzare regole di analisi speciali per i suoi argomenti. Quindi, ahimè, il mio voto va a Giovanni 1024.
Ilmari Karonen,

3

[era originariamente solo un comando esterno, un altro nome per /bin/test. Ma alcuni comandi, come [e echo, sono usati così frequentemente negli script di shell che gli implementatori di shell hanno deciso di copiare il codice direttamente nella shell stessa, invece di dover eseguire un altro processo ogni volta che vengono utilizzati. Ciò ha trasformato questi comandi in "builtin", sebbene sia ancora possibile invocare il programma esterno tramite il suo percorso completo.

[[è venuto molto più tardi. Sebbene il built-in sia implementato internamente all'interno della shell, viene analizzato proprio come i comandi esterni. Come spiegato nella risposta di John1024, ciò significa che le variabili non quotate eseguiranno la suddivisione delle parole su di esse e i token gradiranno >e <verranno elaborati come reindirizzamento I / O. Ciò ha reso scomoda la scrittura di espressioni di confronto complesse. [[è stato creato come sintassi della shell, in modo che potesse essere analizzato ideosincraticamente. All'interno delle [[variabili non viene suddivisa la parola <e >può essere utilizzata come operatore di confronto, =può comportarsi in modo diverso a seconda che il parametro successivo sia quotato o meno, ecc. Queste sono tutte comodità che rendono [[più facile da usare rispetto al [comando / builtin tradizionale .

Non potevano semplicemente ricodificare [come sintassi come questa perché sarebbe stata una modifica incompatibile a milioni di script. Usando la nuova [[sintassi, che in precedenza non esisteva, potevano completamente rinnovare il modo in cui è usata in un modo compatibile verso l'alto.

Questo è simile all'evoluzione che ha portato alla $((...))sintassi delle espressioni aritmetiche, che ha sostituito per lo più il exprcomando tradizionale .


0

Il più recente [[di bashè un'ottimizzazione di [.

Il classico [ha un grande svantaggio, quando viene usato frequentemente per fare un'operazione banale: genererà ogni volta un nuovo processo:
(Crea un nuovo spazio di indirizzi solo per il confronto 0e 1! Ogni volta!)

Penso che il punto principale dell'aggiunta sia [[stato quello di rendere la valutazione dell'espressione all'interno [non spawn un processo aggiuntivo. Ma il modo in cui [funziona non potrebbe essere cambiato: creerebbe molta confusione e problemi. Quindi, l'ottimizzazione è stata implementata con un nuovo nome, in modo più efficiente, ovvero un comando incorporato nella shell.
È diventato una parola chiave nella sintassi della shell come effetto collaterale.

All'epoca [veniva usato per primo, era il modo giusto di farlo con un processo esterno.


5
Si noti che sebbene [fosse originariamente un comando esterno, è stato aggiunto come integrato nella shell molto presto, probabilmente al momento del rilascio di Unix System III e sicuramente prima del rilascio di Unix System V. Quindi, il 'processo extra' non è stato un problema per anni. Tuttavia, la sintassi è rimasta invariata: è stata trattata come se fosse un comando esterno.
Jonathan Leffler,

@JonathanLeffler Oh, grazie, mi sono perso [entrambi - questo significa alcuni cambiamenti ...
Volker Siegel,
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.