Espansione automatica variabile all'interno del comando bash [[]]


13

Quando si dereferenzia una variabile in bash, è necessario utilizzare il $segno. Tuttavia, sembra che quanto segue funzioni bene:

x=5
[[ x -gt 2 ]]

Qualcuno può spiegarlo?

Modifica: (maggiori informazioni)

Quello che voglio dire è come e perché il comando [[]] sta dereferenziando la mia variabile x senza il segno $. E sì, se x = 1, l'istruzione viene valutata su false (stato di ritorno 1)


2
Cosa intendi con "lavorare bene"? E la tua valutazione cambia se x=1segui [[ x -gt 2]]?
Nohillside,

Voglio dire: come e perché il comando [[]] sta dereferenziando la mia variabile x senza il segno $. E sì, se x = 1, l'istruzione è falsa (stato di ritorno 1)
Ospite

Risposte:


9

Il motivo è che -eqforza una valutazione aritmetica degli argomenti.

Un operatore aritmetico: -eq, -gt, -lt, -ge, -lee -neall'interno di una [[ ]](in ksh, zsh e bash) mezzi per espandere automaticamente i nomi delle variabili, come nel linguaggio C, ha bisogno di un leader $.

  • Per conferma dobbiamo esaminare il codice sorgente di bash. Il manuale non offre una conferma diretta .

    All'interno test.cdel trattamento degli operatori aritmetici ricadono in questa funzione:

    arithcomp (s, t, op, flags)

    Dove se tsono entrambi gli operandi. Gli operandi vengono assegnati a questa funzione:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    La funzione evalexpè definita all'interno expr.c, che ha questa intestazione:

    /* expr.c -- arithmetic expression evaluation. */

    Quindi, sì, entrambi i lati di un operatore aritmetico rientrano (direttamente) nella valutazione dell'espressione aritmetica. Direttamente, senza ma, senza se.


In pratica, con:

 $ x=3

Entrambi falliscono:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

Che è corretto, xnon viene espanso e xnon è uguale a un numero.

Tuttavia:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

La variabile denominata xviene espansa (anche senza $).

Questo non accade per a […]in zsh o bash (lo fa in ksh).


È lo stesso di quello che succede dentro a $((…)):

 $ echo $(( x + 7 ))
 10

E, per favore, comprendi che questo è (molto) ricorsivo (tranne in dash e yash):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

E abbastanza rischioso:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

L'errore di sintassi potrebbe essere facilmente evitato:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Come dice il proverbio: disinfetta il tuo contributo

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

fine di 😮


Sia il (più vecchio) esterno /usr/bin/test(non il builtin test) che il ancora più vecchio e anche esterno exprnon espandono le espressioni solo numeri interi (e apparentemente, solo numeri decimali):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument

Interessante. Non è difficile dire come sia possibile : essendo [[una parola chiave, gli operatori e gli operandi vengono rilevati quando il comando viene letto e non dopo l'espansione. Così [[può trattare -eqin modo più intelligente rispetto, ad esempio, [. Ma quello che mi chiedo è: dove possiamo trovare la documentazione sulla logica che bash usa per interpretare i comandi composti? Non mi sembra abbastanza ovvio e apparentemente non riesco a trovare spiegazioni soddisfacenti in mano info bash.
fra-san,

Bash non lo documenta ovunque io possa trovare. Esiste una specie di descrizione in man ksh93 : Sono consentiti anche i seguenti confronti aritmetici obsoleti: exp1 -eq exp2 . C'è questo testo nella testsezione uomo zshbuiltins operatori aritmetici si aspettano intero argomenti piuttosto che espressioni aritmetiche . Ciò conferma che alcuni argomenti sono trattati come espressioni aritmetiche dal test incorporato in condizioni non specificate in questa citazione. Confermo con il codice sorgente ....
Isaac il

7

Gli operandi dei confronti numerici -eq, -gt, -lt, -ge, -lee -nesono presi come espressioni aritmetiche. Con qualche limitazione, devono ancora essere parole singole shell.

Il comportamento dei nomi delle variabili nell'espressione aritmetica è descritto in Shell Arithmetic :

Le variabili shell sono consentite come operandi; l'espansione dei parametri viene eseguita prima della valutazione dell'espressione. All'interno di un'espressione, le variabili di shell possono anche essere referenziate per nome senza usare la sintassi di espansione dei parametri. Una variabile di shell che è nulla o non impostata viene valutata su 0 quando viene referenziata per nome senza utilizzare la sintassi di espansione del parametro.

e anche:

Il valore di una variabile viene valutato come espressione aritmetica quando viene referenziato

Ma in realtà non riesco a trovare la parte della documentazione in cui si dice che i confronti numerici assumano espressioni aritmetiche. Non è descritto in Costrutti condizionali sotto [[, né è descritto in Espressioni condizionali di Bash .

Ma, per esperimento, sembra funzionare come detto sopra.

Quindi, cose come questa funzionano:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

anche questo (viene valutato il valore della variabile):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Ma questo no; non è una singola parola shell quando [[ .. ]]viene analizzato, quindi c'è un errore di sintassi nel condizionale:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

In altri contesti aritmetici, non è necessario che l'espressione sia senza spazi bianchi. Questo stampa 999, poiché le parentesi delimitano in modo inequivocabile l'espressione aritmetica nell'indice:

a[6]=999; echo ${a[1 + 2 + 3]}

D'altra parte, il =confronto è una corrispondenza del modello e non coinvolge l'aritmetica, né l'espansione automatica variabile effettuata in un contesto aritmetico (costrutti condizionali):

Quando vengono utilizzati gli operatori ==e !=, la stringa a destra dell'operatore viene considerata un modello e abbinata secondo le regole descritte di seguito in Pattern Matching, come se l'opzione shell extglob fosse abilitata. L' =operatore è identico a ==.

Quindi questo è falso poiché le stringhe sono ovviamente diverse:

[[ "1 + 2 + 3" = 6 ]] 

così com'è, anche se i valori numerici sono gli stessi:

[[ 6 = 06 ]] 

e anche qui le stringhe ( xe 6) vengono confrontate, sono diverse:

x=6
[[ x = 6 ]]

Questo espanderebbe la variabile, tuttavia, quindi questo è vero:

x=6
[[ $x = 6 ]]

non riesco effettivamente a trovare la parte della documentazione in cui si dice che i confronti numerici assumano espressioni aritmetiche. La conferma è nel codice .
Isacco il

La cosa più vicina è che la descrizione di arg1 OP arg2dice che gli arg possono essere numeri interi positivi o negativi, cosa che suppongo implichi che sono trattati come espressioni aritmetiche. Confusamente, implica anche che non possono essere zero. :)
Barmar il

@Barmar, ehh, giusto. Ma questo vale anche per i confronti numerici [, e lì non sono espressioni aritmetiche. Invece, Bash si lamenta di non numeri interi.
ilkkachu,

@ilkkachu [è un comando esterno, non ha accesso alle variabili della shell. Spesso è ottimizzato con un comando integrato, ma si comporta comunque allo stesso modo.
Barmar,

@Barmar, intendevo dire che la frase "Arg1 e arg2 può essere numeri interi positivi o negativi". appare in Espressioni condizionali di Bash e tale elenco si applica [anche a [[. Anche con [, gli operandi -eqe gli amici devono / devono essere numeri interi, quindi vale anche la descrizione. Prendendo "deve essere numeri interi" per indicare "sono interpretati come espressioni aritmetiche" non si applica in entrambi i casi. (Probabilmente almeno in parte per [comportarsi come un normale comando, come dici tu.)
ilkkachu,

1

Sì, l'osservazione è corretta, l'espansione variabile viene eseguita sulle espressioni tra parentesi doppie [[ ]], quindi non è necessario inserire $un nome di variabile.

Questo è esplicitamente dichiarato nel bashmanuale:

[[espressione]]

(...) La suddivisione delle parole e l'espansione del percorso non vengono eseguite sulle parole tra [[e]]; vengono eseguite l'espansione della tilde, l'espansione dei parametri e delle variabili, l'espansione aritmetica, la sostituzione dei comandi, la sostituzione del processo e la rimozione delle quote.

Si noti che questo non è il caso della versione a parentesi singola [ ], in quanto [non è una parola chiave di shell (sintassi), ma piuttosto un comando (in bash è incorporato, altre shell potrebbero usare esterne, allineate per testare).


1
Grazie per avermi risposto. Sembra che questo funzioni solo per i numeri. x = city [[$ x == city]] Questo non funziona senza il simbolo $.
Ospite il

3
Sembra che ci sia di più qui: (x=1; [[ $x = 1 ]]; echo $?)restituisce 0, (x=1; [[ x = 1 ]]; echo $?)restituisce 1, ovvero l'espansione dei parametri non viene eseguita xquando si confrontano le stringhe. Questo comportamento sembra una valutazione aritmetica innescata dall'espansione aritmetica, ovvero cosa succede in (x=1; echo $((x+1))). (A proposito della valutazione aritmetica, man bashafferma che "All'interno di un'espressione, le variabili di shell possono anche essere referenziate per nome senza usare la sintassi di espansione dei parametri.)
fra-san

@ fra-san In effetti, poiché l' -gtoperatore si aspetta il numero, quindi l'intera espressione viene rivalutata come se fosse dentro (()), d'altra parte si ==aspetta stringhe, quindi viene attivata la funzione di corrispondenza del modello. Non ho scavato nel codice sorgente, ma sembra ragionevole.
jimmij,

[è una shell integrata in bash.
Nizam Mohamed,

1
@NizamMohamed È incorporato, ma non è ancora una parola chiave.
Kusalananda
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.