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?
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?
Risposte:
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.
[[ ]](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.
Differenze comportamentali
Testato in Bash 4.3.11:
Estensione POSIX vs Bash:
[ è POSIX[[è un'estensione di Bash¹ documentata su: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructscomando 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
<
[[ a < b ]]: confronto lessicografico[ a \< b ]: Come sopra. \richiesto altrimenti esegue il reindirizzamento come per qualsiasi altro comando. Estensione Bash.expr a \< b > /dev/null: POSIX equivalente², vedi: Come testare stringhe lessicografiche inferiori o uguali in Bash?&& 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 5suddivisione delle parole e generazione del nome del file in caso di espansioni (split + glob)
x='a b'; [[ $x = 'a b' ]]: vero, virgolette non necessariex='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 [[ ]]:
[è 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.
[] 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?'
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.
[[è una parola chiave bash simile al [comando (ma più potente di) .
Vedere
http://mywiki.wooledge.org/BashFAQ/031 e http://mywiki.wooledge.org/BashGuide/TestsAndConditionals
A meno che tu non stia scrivendo per POSIX sh, ti consigliamo [[.
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)
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 [])