Qual è la differenza tra gli operatori Bash [[vs [vs (vs ((?

Sono un po confuso su cosa fanno questi operatori in modo diverso quando usato in bash (parentesi, doppie parentesi, parentesi e doppie parentesi).

[[ , [ , ( , (( 

Ho visto persone usarle su istruzioni if come questa:

if [[condition]] if [condition] if ((condition)) if (condition) 

Commenti

Rispondi

Nelle shell simili a Bourne, unistruzione if ha in genere il seguente aspetto

if command-list1 then command-list2 else command-list3 fi 

La clausola then viene eseguita se il codice di uscita del lelenco dei comandi è zero. Se il codice di uscita è diverso da zero, viene eseguita la clausola else. command-list1 può essere semplice o complesso. Ad esempio, può essere una sequenza di una o più pipeline separate da uno degli operatori ;, &, &&, || o nuova riga. Le if condizioni mostrate di seguito sono solo casi speciali di command-list1:

  1. if [ condition ]

    [ è un altro nome per il tradizionale comando test. [ / test è unutilità POSIX standard. Tutte le shell POSIX lo hanno integrato (sebbene non sia richiesto da POSIX²). Il comando test imposta un codice di uscita e listruzione if agisce di conseguenza. I test tipici sono se un file esiste o un numero è uguale a un altro.

  2. if [[ condition ]]

    Questa è una nuova variante aggiornata di test ¹ da ksh che bash , zsh , yash , supporta anche busybox sh . Questo costrutto [[ ... ]] imposta anche un codice di uscita e if agisce di conseguenza. Tra le sue funzionalità estese, può verificare se una stringa corrisponde a un pattern di caratteri jolly (non in busybox sh ).

  3. if ((condition))

    Unaltra estensione ksh supportata anche da bash e zsh . Questo esegue operazioni aritmetiche. Come risultato dellaritmetica, viene impostato un codice di uscita e listruzione if agisce acco rdingly. Restituisce un codice di uscita zero (vero) se il risultato del calcolo aritmetico è diverso da zero. Come [[...]], questo modulo non è POSIX e quindi non portabile.

  4. if (command)

    Questo esegue il comando in una subshell. Quando il comando viene completato, imposta un codice di uscita e listruzione if agisce di conseguenza.

    Un motivo tipico per usare una sottoshell come questa è limitare gli effetti collaterali di command se command ha richiesto assegnazioni di variabili o altre modifiche allambiente della shell. Tali modifiche non rimangono dopo il completamento della subshell.

  5. if command

    viene eseguito e listruzione if agisce in base al suo codice di uscita.


¹ sebbene non sia realmente un comando ma uno speciale costrutto di shell con una propria sintassi separata da quella del comando normale, e che variano in modo significativo tra le implementazioni della shell

² POSIX richiede che ci sia un test e [ utilità del sistema tuttavia, sebbene nel caso di [, molte distribuzioni Linux siano note per essere m emettendolo.

Commenti

  • Grazie per aver incluso la quinta opzione. Questa ‘ è la chiave per capire come funziona effettivamente ed è sorprendentemente sottoutilizzata.
  • Tieni presente che [ è in realtà un binario, non un comando interno o un simbolo. Generalmente vive a /bin.
  • @JulienR. in realtà [ è integrato, così come test. Sono disponibili versioni binarie per motivi di compatibilità. Dai unocchiata a help [ e help test.
  • Vale la pena notare che mentre ((isnt POSIX, $(( cioè lespansione aritmetica è ed è ‘ facile confonderli. Spesso una soluzione alternativa è usare qualcosa come [ $((2+2)) -eq 4 ] per usare laritmetica nelle affermazioni condizionali
  • Vorrei poter votare questa risposta più di una volta.

Risposta

  • (…) le parentesi indicano un subshell . Quello che cè dentro non è unespressione come in molte altre lingue. È un elenco di comandi (proprio come le parentesi esterne). Questi comandi vengono eseguiti in un sottoprocesso separato, quindi qualsiasi reindirizzamento, assegnazione, ecc. Eseguito allinterno delle parentesi non ha effetto al di fuori delle parentesi.
    • Con un interlinea simbolo del dollaro, $(…) è una sostituzione del comando : cè un comando tra parentesi e loutput del comando viene utilizzato come parte della riga di comando (dopo espansioni aggiuntive a meno che la sostituzione non sia tra virgolette doppie, ma “s unaltra storia ).
  • { … } le parentesi graffe sono come le parentesi in quanto raggruppano i comandi, ma influenzano solo lanalisi, non il raggruppamento. Il programma x=2; { x=4; }; echo $x ne stampa 4, mentre x=2; (x=4); echo $x ne stampa 2. (Anche le parentesi graffe che sono parole chiave devono essere delimitate e trovato nella posizione del comando (da qui lo spazio dopo { e ; prima di }) mentre le parentesi non “t. Questa è solo una stranezza della sintassi).
    • Con un segno di dollaro iniziale, ${VAR} è un espansione parametro , espandibile al valore di una variabile, con possibili trasformazioni extra. La shell ksh93 supporta anche ${ cmd;} come forma di sostituzione del comando che non genera una subshell.
  • ((…)) doppie parentesi circondano un istruzione aritmetica , ovvero un calcolo su numeri interi, con una sintassi simile ad altri linguaggi di programmazione. Questa sintassi è usata principalmente per gli assegnamenti e nei condizionali. Esiste solo in ksh / bash / zsh, non in semplice sh.
    • La stessa sintassi è usata nelle espressioni aritmetiche $((…)), che si espande al valore intero dellespressione.
  • [ … ] racchiude singole parentesi espressioni condizionali . Le espressioni condizionali sono per lo più basate su operatori come -n "$variable" per verificare se una variabile è vuota e -e "$file" per verificare se esiste un file. Tieni presente che è necessario uno spazio attorno a ciascuna operatore (ad es. [ "$x" = "$y" ], non [ "$x"="$y" ] ) e uno spazio o un carattere come ; sia allinterno che allesterno delle parentesi (ad es. [ -n "$foo" ], non [-n "$foo"] ).
  • [[ … ]] le doppie parentesi sono una forma alternativa di espressioni condizionali in ksh / bash / zsh con alcune funzionalità aggiuntive, ad esempio puoi scrivere [[ -L $file && -f $file ]] per verificare se un file è un collegamento simbolico a un file normale mentre le parentesi singole richiedono [ -L "$file" ] && [ -f "$file" ]. Vedere Perché lespansione dei parametri con spazi senza virgolette funziona allinterno di doppie parentesi [[ma non singole parentesi [? per ulteriori informazioni su questo argomento.

Nella shell, ogni comando è un comando condizionale: ogni comando ha uno stato di ritorno che è 0 che indica successo o un numero intero compreso tra 1 e 255 (e potenzialmente di più in alcune shell) che indica fallimento. Il [ … ] comando (o [[ … ]] sintassi) è un comando particolare che può essere scritto anche test … e riesce quando esiste un file, o quando una stringa non è vuota, o quando un numero è più piccolo di un altro, ecc. Il modulo di sintassi ((…)) ha successo quando un numero è diverso da zero .Di seguito sono riportati alcuni esempi di condizionali in uno script di shell:

  • Verifica se myfile contiene la stringa hello:

    if grep -q hello myfile; then … 
  • Se mydir è una directory, cambia in e fai cose:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Verifica se esiste un file chiamato myfile nella directory corrente:

    if [ -e myfile ]; then … 
  • Lo stesso, ma include anche collegamenti simbolici penzolanti:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Verifica se il valore di x (che si presume sia numerico) è almeno 2, portabile:

    if [ "$x" -ge 2 ]; then … 
  • Verifica se il valore di x (che si presume sia numerico) è almeno 2, in bash / ksh / zsh:

    if ((x >= 2)); then … 

Commenti

  • Tieni presente che la parentesi singola supporta -a invece di &&, quindi si può scrivere: [ -L $file -a -f $file ], che è lo stesso numero di caratteri tra parentesi senza i [ e ]
  • @AlexisWilke Gli operatori -a e -o sono problematico perché possono portare ad analisi errate se alcuni degli operandi coinvolti sembrano operatori. Questo ‘ è il motivo per cui non ‘ li menziono: hanno zero vantaggio e non ‘ Funziona sempre. E non scrivere mai espansioni di variabili non quotate senza una buona ragione: [[ -L $file -a -f $file ]] va bene ma con singole parentesi hai bisogno di [ -L "$file" -a -f "$file" ] (che va bene, ad es. div id = “fbec67d83e”>

inizia sempre con/o./).

  • Tieni presente che ‘ s [[ -L $file && -f $file ]] (no -a con [[...]] variante).
  • Risposta

    [ vs [[

    Questa risposta coprirà il [ vs [[ sottoinsieme della domanda.

    Alcune differenze su Bash 4.3.11:

    • POSIX vs estensione Bash:

    • comando normale vs magia

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

        ] è solo lultimo argomento di [.

      Ubuntu 16.04 ha effettivamente un eseguibile su /usr/bin/[ fornito da coreutils , ma la versione incorporata di bash ha la precedenza.

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

      In particolare, < è il reindirizzamento, && e || concatena più comandi, ( ) genera subshell a meno che non sia sfuggito a \ e lespansione delle parole avviene come al solito.

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

        Ci sono anche ulteriori differenze come = e =~ .

      In Bashese: [ è un comando integrato e [[ è 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 AND cmd1 && cmd2
      • [ a = a ] && [ b = b ]: equivalente affidabile POSIX
      • [ a = a -a b = b ]: quasi equivalente, ma deprecato da POSIX perché è folle e fallisce per alcuni valori di a o b come ! o ( che verrebbe interpretato come operazioni logiche
    • (

      • [[ (a = a || a = b) && a = b ]]: false. Senza ( ), sarebbe vero perché [[ && ]] ha una precedenza maggiore di [[ || ]]
      • [ ( a = a ) ]: errore di sintassi, () viene interpretato come una subshell
      • [ \( a = a -o a = b \) -a a = b ]: equivalente, ma (), -a e -o sono obsoleti di POSIX. Senza \( \) sarebbe vero perché -a ha una precedenza maggiore di -o
      • { [ a = a ] || [ a = b ]; } && [ a = b ] equivalente POSIX non obsoleto. In questo caso particolare, tuttavia, avremmo potuto scrivere solo: [ a = a ] || [ a = b ] && [ a = b ] perché || e && gli operatori di shell hanno la stessa precedenza a differenza di [[ || ]] e [[ && ]] e -o, -a e [
    • suddivisione delle parole e generazione del nome del file dopo le espansioni (split + glob )

      • x="a b"; [[ $x = "a b" ]]: true, virgolette non necessarie
      • x="a b"; [ $x = "a b" ]: sintassi errore, 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? ]]: true, perché corrispondenza di pattern ( * ? [ sono magici). Non si espande glob in f iles 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 glob
      • = e == sono gli stessi in [ e [[, ma == è unestensione Bash.
      • case ab in (a?) echo match; esac: equivalente POSIX
      • [[ ab =~ "ab?" ]]: false, perde magia con "" in Bash 3.2 e versioni successive e la compatibilità fornita con bash 3.1 non è abilitata (come con BASH_COMPAT=3.1)
      • [[ ab? =~ "ab?" ]]: true
    • =~

      • [[ ab =~ ab? ]]: true, POSIX esteso espressione regolare corrisponde, ? non si espande globalmente
      • [ a =~ a ]: errore di sintassi. Nessun equivalente bash.
      • printf "ab\n" | grep -Eq "ab?": equivalente POSIX (solo dati a riga singola)
      • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": Equivalente POSIX.

    Consiglio: utilizza sempre []

    Ci sono equivalenti POSIX per ogni [[ ]] costrutto che ho “visto.

    Se usi [[ ]] tu:

    • perdi la portabilità
    • costringi il lettore a imparare le complessità di unaltra estensione bash. [ è solo un normale comando con un nome strano, non è coinvolta alcuna semantica speciale.

    Grazie a Stéphane Chazelas per importanti correzioni e aggiunte.

    Commenti

    • @St é phaneChazelas grazie per le informazioni! ‘ ho aggiunto expr alla risposta. Il termine ” Bash ex tension ” non significa che Bash sia stata la prima shell ad aggiungere un po di sintassi, imparare POSIX sh vs Bash è già abbastanza per farmi impazzire.
    • Vedi man test se hai provato man [ e ti sei perso. Questo spiegherà la variante POSIX.
    • Correzione: ] è un argomento del comando [, ma non ‘ t impedisce lutilizzo di ulteriori argomenti. ] deve essere l ultimo argomento di [, ma può anche essere utilizzato come parte dellespressione di prova. Ad esempio, if [ "$foo" = ] ]; then verificherà se la variabile foo è impostata su “] ” (come if [ ] = "$foo" ]; then).
    • @GordonDavisson grazie, non lho fatto ‘ lo so, risolto.
    • @ tgm1024 – Monicawasmistreated sì, anche questa è una considerazione valida.

    Risposta

    Dalla documentazione di bash :

    Lelenco (list) viene eseguito in un ambiente subshell (vedere AMBIENTE DI ESECUZIONE DEI COMANDI di seguito). Le assegnazioni di variabili e i comandi incorporati che influenzano lambiente della shell non rimangono in vigore dopo il completamento del comando. Lo stato di ritorno è lo stato di uscita di list.

    In altre parole, assicurati che qualunque cosa accada in “list” (come un cd) non ha alcun effetto al di fuori di ( e ). Lunica cosa che trapelerà è il codice di uscita dellultimo comando o con set -e il primo comando che genera un errore (altro di alcuni come if, while e così via)

    ((expression)) Lespressione viene valutata in base alle regole descritte di seguito in VALUTAZIONE ARITMETICA. Se il valore dellespressione è diverso da zero, lo stato di ritorno è 0; altrimenti lo stato di ritorno è 1. Ciò equivale esattamente a lasciare ” espressione “.

    Questa è unestensione bash che ti consente di fare matematica. È in qualche modo simile allutilizzo di expr senza tutte le limitazioni di expr (ad esempio avere spazi ovunque, sfuggire a *, ecc.)

    [[ expression ]] Restituisce uno stato di 0 o 1 a seconda del valutazione dellespressione dellespressione condizionale. Le espressioni sono composte dalle primarie descritte di seguito in ESPRESSIONI CONDIZIONALI. La suddivisione delle parole e lespansione del nome del percorso non vengono eseguite sulle parole tra [[e]]; Vengono eseguite lespansione della tilde, lespansione di parametri e variabili, lespansione aritmetica, la sostituzione dei comandi, la sostituzione dei processi e la rimozione delle quote. Gli operatori condizionali come -f devono essere non quotati per essere riconosciuti come primari.

    Quando viene utilizzato con [[, < e > gli operatori ordinano lessicograficamente utilizzando le impostazioni internazionali correnti.

    Questo offre un test avanzato per confrontare stringhe, numeri e file un po come test offre, ma più potente.

    [ expr ] Restituisce uno stato di 0 (vero) o 1 (falso) a seconda della valutazione dellespressione condizionale expr. Ogni operatore e oper e deve essere un argomento separato. Le espressioni sono composte dalle primarie descritte sopra in ESPRESSIONI CONDIZIONALI. test non accetta alcuna opzione, né accetta e ignora un argomento di – come indicante la fine delle opzioni.

    […]

    Questo chiama test. In realtà, ai vecchi tempi, [ era un collegamento simbolico a test. Funziona allo stesso modo e hai le stesse limitazioni. Poiché un binario conosce il nome con cui è stato avviato, il programma di test sa quando è stato avviato come [ e può ignorare il suo ultimo parametro, che dovrebbe essere ]. Trucchi divertenti per Unix.

    Tieni presente che in caso di bash, [ e test sono funzioni integrate (come menzionato in un commento), ma si applicano più o meno le stesse limitazioni.

    Commenti

    • Sebbene test e [ sono ovviamente comandi incorporati in Bash, ma ‘ è probabile che un Anche il binario esterno esiste.
    • Il binario esterno per [ non è un collegamento simbolico a test sulla maggior parte dei sistemi moderni .
    • In qualche modo, trovo divertente che si prendano la briga di creare due binari separati, che hanno entrambi esattamente ciò di cui hanno bisogno, invece di combinarli e aggiungere un paio di condizionali. Anche se in realtà strings /usr/bin/test mostra che ha anche il testo della guida, quindi non ‘ non so cosa dire.
    • @ Random832 Capisco il tuo punto sulla logica GNU per evitare un comportamento arg0 imprevisto, ma sui requisiti POSIX, ‘ t essere così affermativo. Sebbene il comando test sia ovviamente richiesto per esistere come comando autonomo basato su file dallo standard, nulla in esso afferma che la sua variante [ deve essere implementato anche in questo modo. Ad esempio, Solaris 11 non ‘ t fornisce alcun [ eseguibile ma è comunque completamente conforme agli standard POSIX
    • (uscita 1) ha un effetto al di fuori delle parentesi.

    Risposta

    Alcuni esempi:

    Test tradizionale:

    foo="some thing" # check if value of foo is not empty if [ -n "$foo" ] ; then... if test -n "$foo" ; then... 

    test e [ sono comandi come tutti gli altri, quindi la variabile è divisa in parole a meno che non sia tra virgolette.

    Test nuovo stile

    [[ ... ]] è un (più recente) costrutto di shell speciale, che funziona in modo leggermente diverso, la cosa più ovvia è che non “t variabili suddivise in parole:

    if [[ -n $foo ]] ; then... 

    Alcuni documentazione su [ e [[ qui .

    Test aritmetico:

    foo=12 bar=3 if (( $foo + $bar == 15 )) ; then ... 

    “Normale “commands:

    Tutti i comandi precedenti agiscono come normali comandi e if può accettare qualsiasi comando:

    # grep returns true if it finds something if grep pattern file ; then ... 

    Comandi multipli:

    Oppure possiamo usare più comandi. Il wrapping di una serie di comandi in ( ... ) li esegue in una subshell, creando una copia temporanea dello stato della shell (directory di lavoro, variabili). Se necessario per eseguire temporaneamente un programma in unaltra directory:

    # this will move to $somedir only for the duration of the subshell if ( cd $somedir ; some_test ) ; then ... # while here, the rest of the script will see the new working # directory, even after the test if cd $somedir ; some_test ; then ... 

    Rispondi

    Comandi di raggruppamento

    Bash fornisce due modi per raggruppare un elenco di comandi da eseguire come ununità.

    ( list ) Posizionando un elenco di comandi tra parentesi, viene creato un ambiente di subshell e ciascuno dei comandi in elenco da eseguire in quella subshell. Poiché lelenco è eseguito in una subshell, le assegnazioni delle variabili non rimangono attive dopo il completamento della subshell.

    $ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a" inside: a=2 outside: a=1 

    { list; } Posizionamento di un elenco di comandi tra parentesi graffe fa sì che lelenco venga eseguito nel contesto della shell corrente . Non viene creata alcuna subshell. Il seguente elenco di punti e virgola (o nuova riga) è obbligatorio. Sorgente

    ${} Parameter expansion Ex: ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s $() Command substitution Ex: result=$(COMMAND) $(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

    Costrutti condizionali

    Staffa singola ie []
    Per confronto ==, !=, <, e > e dovrebbero essere utilizzati e per il confronto numerico eq, ne,lt e gt dovrebbe essere utilizzato.

    Parentesi migliorate ie [[]]

    In tutti gli esempi precedenti, abbiamo usato solo parentesi singole per racchiudere lespressione condizionale, ma bash consente le doppie parentesi che servono come una versione migliorata della sintassi della singola parentesi.

    Per confronto ==, !=, <, e > possono essere usati letteralmente.

    • [ è un sinonimo di comando di prova. Anche se è integrato nella shell, crea un nuovo processo.
    • [[ è una nuova versione migliorata di esso, che è una parola chiave, non un programma .
    • [[ è compreso da Korn e Bash.

    Sorgente

    Lascia un commento

    Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *