Qual è la differenza tra parentesi quadre doppie e singole in bash?


427

Mi chiedevo solo quale fosse esattamente la differenza tra

[[ $STRING != foo ]]

e

[ $STRING != foo ]

è, a parte questo, che è conforme a posix, trovato in sh e il primo è un'estensione trovata in bash.


1
Nel caso ti stavi anche chiedendo di non usare parentesi, per esempio nel contesto di una ifdichiarazione, vedi mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev

inoltre, dai documenti di Ubuntu: wiki.ubuntu.com/…
radistao,

Risposte:


310

Ci sono diverse differenze. Secondo me, alcuni dei più importanti sono:

  1. [è incorporato in Bash e in molte altre conchiglie moderne. Il builtin [è simile al testrequisito aggiuntivo di una chiusura ]. I builtin [e testimitano la funzionalità /bin/[e /bin/testinsieme ai loro limiti in modo che gli script siano retrocompatibili. Gli eseguibili originali esistono ancora principalmente per conformità POSIX e compatibilità con le versioni precedenti. L'esecuzione del comando type [in Bash indica che [viene interpretato come predefinito per impostazione predefinita. (Nota: which [cerca solo gli eseguibili sul PERCORSO ed è equivalente a type -p [)
  2. [[non è così compatibile, non funzionerà necessariamente con qualsiasi /bin/shpunto. Quindi [[è la più moderna opzione Bash / Zsh / Ksh.
  3. Poiché [[è incorporato nella shell e non ha requisiti legacy, non è necessario preoccuparsi della divisione delle parole in base alla variabile IFS per confondere le variabili che valutano una stringa con spazi. Pertanto, non è necessario inserire la variabile tra virgolette doppie.

Per la maggior parte, il resto è solo una sintassi migliore. Per vedere più differenze, consiglio questo link a una risposta alle domande frequenti: qual è la differenza tra test, [e [[? . In effetti, se prendi sul serio lo script bash, ti consiglio di leggere l'intero wiki , comprese le FAQ, le insidie e la guida. La sezione test della sezione guida spiega anche queste differenze e perché gli autori pensano che [[sia una scelta migliore se non devi preoccuparti di essere portatile. Le ragioni principali sono:

  1. Non devi preoccuparti di citare il lato sinistro del test in modo che venga effettivamente letto come variabile.
  2. Non devi scappare di meno e di più rispetto < >alle barre rovesciate per evitare che vengano valutate come reindirizzamento di input, che può davvero rovinare alcune cose sovrascrivendo i file. Questo torna ad [[essere incorporato. Se [(test) è un programma esterno, la shell dovrebbe fare un'eccezione nel modo in cui valuta <e >solo se /bin/testviene chiamata, il che non avrebbe davvero senso.

5
Grazie, il link alle FAQ di bash era quello che stavo cercando (non sapevo di quella pagina, grazie).
0x89,

2
Ho modificato il tuo post con queste informazioni, ma [e test sono eseguiti come builtin. I builtin sono stati progettati per sostituire / bin / [e / bin / test, ma dovevano anche riprodurre i limiti dei binari. Il comando 'type [' verifica che venga utilizzato l'integrato. 'che [' cerca solo gli eseguibili sul PERCORSO ed equivale a 'tipo -P ['
klynch,

133

In breve:

[è un builth bash

[[]] sono parole chiave bash

Parole chiave: le parole chiave sono abbastanza simili ai builtin, ma la differenza principale è che a loro si applicano regole di analisi speciali. Ad esempio, [è un built-in bash, mentre [[è una parola chiave bash. Sono entrambi usati per testare le cose, ma poiché [[è una parola chiave anziché un built-in, trae vantaggio da alcune regole di analisi speciali che lo rendono molto più semplice:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Il primo esempio restituisce un errore perché bash tenta di reindirizzare il file b al comando [a]. Il secondo esempio in realtà fa quello che ti aspetti. Il carattere <non ha più il suo significato speciale di operatore Reindirizzamento file.

Fonte: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments


3
[è un comando shell POSIX; non è necessario che sia incorporato. ]è solo un argomento che quel comando cerca, in modo che la sintassi sia bilanciata. Il comando è sinonimo di testtranne che testnon cerca una chiusura ].
Kaz,


81

Differenze comportamentali

Alcune differenze su Bash 4.3.11:

  • Estensione POSIX vs Bash:

  • comando regolare vs magia

    • [ è solo un comando regolare con un nome strano.

      ]è solo un argomento [che impedisce l'utilizzo di ulteriori argomenti.

      Ubuntu 16.04 in realtà ha un eseguibile per esso /usr/bin/[fornito da coreutils, ma la versione integrata di bash ha la precedenza.

      Nulla è alterato nel modo in cui Bash analizza il comando.

      In particolare, <è il reindirizzamento &&e ||concatena più comandi, ( )genera sottotitoli a meno che non sia evaso da \, e l'espansione delle parole avviene come al solito.

    • [[ X ]]è un singolo costrutto che consente di Xanalizzarlo magicamente. <, &&, ||E ()sono trattati in modo speciale, e le regole suddivisione in parole sono diverse.

      Ci sono anche ulteriori differenze come =e =~.

      In bashese: [è un comando integrato ed [[è una parola chiave: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && e ||

    • [[ a = a && b = b ]]: vero, logico e
    • [ a = a && b = b ]: errore di sintassi, &&analizzato come separatore di comandi ANDcmd1 && cmd2
    • [ a = a -a b = b ]: equivalente, ma deprecato da POSIX³
    • [ a = a ] && [ b = b ]: POSIX ed equivalente affidabile
  • (

    • [[ (a = a || a = b) && a = b ]]: falso
    • [ ( a = a ) ]: errore di sintassi, ()viene interpretato come una subshell
    • [ \( a = a -o a = b \) -a a = b ]: equivalente, ma ()è deprecato da POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX equivalente 5
  • suddivisione delle parole e generazione del nome del file in caso di espansioni (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: vero, virgolette non necessarie
    • x='a b'; [ $x = 'a b' ]: errore di sintassi, si espande in [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: errore di sintassi se c'è più di un file nella directory corrente.
    • x='a b'; [ "$x" = 'a b' ]: Equivalente POSIX
  • =

    • [[ ab = a? ]]: vero, perché corrisponde al modello ( * ? [sono magici). Glob non si espande nei file nella directory corrente.
    • [ ab = a? ]: a?glob si espande. Quindi può essere vero o falso a seconda dei file nella directory corrente.
    • [ ab = a\? ]: false, non espansione globale
    • =e ==sono uguali in entrambi [e [[, ma ==è un'estensione di Bash.
    • case ab in (a?) echo match; esac: Equivalente POSIX
    • [[ ab =~ 'ab?' ]]: false 4 , perde la magia con''
    • [[ ab? =~ 'ab?' ]]: vero
  • =~

    • [[ ab =~ ab? ]]: true, la corrispondenza delle espressioni regolari estese POSIX ?non si espande a livello globale
    • [ a =~ a ]: Errore di sintassi. Nessun equivalente bash.
    • printf 'ab\n' | grep -Eq 'ab?': Equivalente POSIX (solo dati a linea singola)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Equivalente POSIX.

Raccomandazione : usare sempre [].

Esistono equivalenti POSIX per ogni [[ ]]costrutto che ho visto.

Se ti usi [[ ]]:

  • perdere la portabilità
  • costringere il lettore a imparare le complessità di un'altra estensione bash. [è solo un comando regolare con un nome strano, nessuna semantica speciale è coinvolta.

¹ Ispirato al [[...]]costrutto equivalente nella shell Korn

² ma non riesce per alcuni valori di ao b(come +o index) e fa un confronto numerico se ae bsembra numeri decimali. expr "x$a" '<' "x$b"funziona intorno a entrambi.

³ e fallisce anche per alcuni valori di ao bcome !o (.

4 in bash 3.2 e versioni successive e la compatibilità fornita con bash 3.1 non è abilitata (come con BASH_COMPAT=3.1)

5 se il raggruppamento (qui con il {...;}gruppo di comando anziché (...)che sarebbe in una subshell inutile) non è necessaria in quanto i ||e &&operatori guscio (diversamente le ||e && [[...]]operatori oi -o/ -a [operatori) hanno uguale precedenza. Quindi [ a = a ] || [ a = b ] && [ a = b ]sarebbe equivalente.


Come usare printf 'ab' | grep -Eq 'ab?'all'interno if [ … ]?
meeDamian,

1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []è un comando proprio come grep. La ()non può essere necessario in questo comando non sono sicuro: ho aggiunto che a causa della |, dipende da come Bash analizza le cose. Se non ci fosse, |sono sicuro che puoi scrivere solo if cmd arg arg; then.
Ciro Santilli 25 改造 中心 法轮功 六四 事件


1
Bella lista! Vedi anche: wiki.ubuntu.com/…
radistao,

5

La singola parentesi cioè []è conforme alla shell POSIX per racchiudere un'espressione condizionale.

Le doppie parentesi cioè [[]]sono una versione migliorata (o estensione) della versione POSIX standard, supportata da bash e altre shell (zsh, ksh).

In bash, per il confronto numerico utilizzato eq, ne, lte gt, con doppie parentesi quadre per il confronto possiamo usare ==, !=, <,e >letteralmente.

  • [è sinonimo di comando test. Anche se è integrato nella shell, crea un nuovo processo.
  • [[ è una nuova versione migliorata, che è una parola chiave, non un programma.

per esempio:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works

4

Sulla base di una rapida lettura delle sezioni pertinenti della pagina man, la differenza principale sembra essere che gli operatori ==e !=corrispondono a un modello, piuttosto che a una stringa letterale, e anche che esiste l' =~operatore di confronto regex.

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.