Differenza tra parentesi quadre singole e doppie in Bash


161

Sto leggendo esempi bash ifma alcuni esempi sono scritti con parentesi quadre singole:

if [ -f $param ]
then
  #...
fi

altri con parentesi quadre doppie:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Qual è la differenza?


3
Puoi ottenere la tua risposta guardando la risposta a questa domanda: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Risposte:


188

Single []sono test di condizione conformi alla shell posix.

I doppi [[]]sono un'estensione dello standard []e sono supportati da bash e altre shell (ad esempio zsh, ksh). Supportano operazioni extra (così come le operazioni posix standard). Ad esempio: ||anziché -oe regex corrispondenti a =~. Un elenco più completo delle differenze è disponibile nella sezione del manuale di bash sui costrutti condizionali .

Utilizzare []ogni volta che si desidera che lo script sia portatile su più shell. Utilizzare [[]]se si desidera che le espressioni condizionali non siano supportate da []e non debbano essere portatili.


6
Aggiungo che se il tuo script non inizia con uno shebang che richiede esplicitamente una shell che supporta [[ ]](ad esempio bash con #!/bin/basho #!/usr/bin/env bash), dovresti usare l'opzione portatile. Gli script che assumono / bin / sh supportano estensioni come questa si romperanno su sistemi operativi come le recenti versioni di Debian e Ubuntu dove non è il caso.
Gordon Davisson,

87

Differenze comportamentali

Testato in 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 ha in realtà 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: /ubuntu/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é esegue la corrispondenza dei modelli ( * ? [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 esegue un confronto numerico se ae bassomiglia a 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 &&shell operatori (in contrapposizione agli ||e && [[...]]operatori oi -o/ -a [operatori) hanno priorità equivalente. Quindi [ a = a ] || [ a = b ] && [ a = b ]sarebbe equivalente.


3
" Esistono equivalenti POSIX per ogni costrutto [[]] che ho visto. " Si può dire la stessa cosa di qualsiasi linguaggio Turing Complete sulla faccia del pianeta.
A. Rick,

3
@A.Rick che sarebbe una risposta valida a tutte le domande "Come fare X in lingua Y" SO :-) Certo, c'è un'implicazione "convenientemente" in questa affermazione.
Ciro Santilli 16 冠状 病 六四 事件 法轮功

6
Riepilogo fantastico. Grazie per lo sforzo. Non sono tuttavia d'accordo con la raccomandazione. È portabilità contro sintassi più coerente e potente. Se è possibile richiedere bash> 4 nei propri ambienti, si consiglia la sintassi [[]].
Bernard,

@Downvoters per favore spiega in modo che io possa imparare e migliorare le informazioni :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8
Ottima risposta ma penso che la Raccomandazione : usare sempre[] dovrebbe essere letta come La mia preferenza : usare []se non si vuole perdere la portabilità . Come indicato qui : se la portabilità / conformità a POSIX o BourneShell è un problema, è necessario utilizzare la vecchia sintassi. Se invece lo script richiede BASH, Zsh o KornShell, la nuova sintassi è generalmente più flessibile, ma non necessariamente retrocompatibile. Preferirei andare con [[ ab =~ ab? ]]se posso e non ho preoccupazioni per la retrocompatibilità rispetto aprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo,

14

All'interno di parentesi singole per il test di condizione (ovvero [...]), alcuni operatori come single =sono supportati da tutte le shell, mentre l'uso dell'operatore ==non è supportato da alcune delle shell più vecchie.

All'interno di doppie parentesi per il test di condizione (ovvero [[...]]), non vi è alcuna differenza tra l'utilizzo =o ==in shell vecchie o nuove.

Modifica: dovrei anche notare che: In bash, usa sempre le parentesi doppie [[]] se possibile, perché è più sicuro delle parentesi singole. Illustrerò perché con il seguente esempio:

if [ $var == "hello" ]; then

se $ var risulta essere nullo / vuoto, questo è ciò che vede lo script:

if [ == "hello" ]; then

che romperà la tua sceneggiatura. La soluzione è utilizzare le doppie parentesi o ricordare sempre di inserire virgolette attorno alle variabili ( "$var"). Le parentesi doppie sono una migliore pratica di codifica difensiva.


1
Mettere le virgolette attorno a tutte le letture delle variabili a meno che tu non abbia una buona ragione per non farlo è una pratica di codifica difensiva molto migliore, poiché si applica a tutte le letture di variabili, non solo a quelle in condizioni. Un bug del programma di installazione di iTunes una volta cancellava i file delle persone se il nome del disco rigido conteneva spazi (o qualcosa del genere). Risolve anche il problema che menzioni.
Chai T. Rex,


1

puoi usare le doppie parentesi quadre per la corrispondenza della luce regex, ad esempio:

if [[ $1 =~ "foo.*bar" ]] ; then

(purché la versione di bash in uso supporti questa sintassi)


6
Tranne che hai citato il modello, quindi ora è trattato come una stringa letterale.
ormaaj,

Verissimo. a volte questo mi

1

Manuale di Bash dice:

Se utilizzati con [[, gli operatori '<' e '>' ordinano lessicograficamente utilizzando le impostazioni internazionali correnti. Il comando test utilizza l'ordinamento ASCII.

(Il comando test è identico a [])

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.