C'è qualche differenza sostanziale quando si confronta una variabile come stringa o come int


22

Per curiosità, quando si esegue un confronto di variabili bash (il cui valore è un integer) è possibile testarlo con un valore predefinito dichiarato come into come string.

Script di esempio :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Uscita :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

e

$  ./param.sh blah
$

Entrambe le funzioni si comportano allo stesso modo, quindi mi chiedo se esiste un modo preferito quando si controlla una variabile intera? Vorrei fare un controllo intrispetto intal fatto che è più rigoroso, ma mi chiedo se ci siano degli svantaggi nel farlo string?

In questo caso, f2()è anche più rigoroso il confronto, vale a dire che passare un valore decimale lo interromperà, mentre non f1()lo prenderà nessun problema.


Nota che Bash non ha davvero un tipo di dati Integer. In pratica puoi semplicemente suggerire a Bash di trattare una stringa come un numero intero.
helpermethod,

Risposte:


18

Sì, molte differenze. Ad esempio, =verifica l'uguaglianza esatta delle stringhe, ma -eqvaluta entrambe le espressioni in modo aritmetico prima di verificare l'uguaglianza:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Inoltre, la stringa vuota risulta numericamente uguale a zero:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

E tutta un'altra classe di differenze appare quando si inseriscono gli operatori di confronto - considerando <vs -lt, ad esempio:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Questo perché la stringa "2" è in ordine alfabetico dopo la stringa "10" (poiché 1 precede 2), ma il numero "2" è numericamente inferiore al numero "10".


2
Non dimenticare che c'è anche (( ... ))per operazioni numeriche. (( " 1 " == 1 )) && echo yes || echo norisultati inyes
Patrick

7

Il confronto tra numero intero e stringa diventa più significativo quando si confronta un valore maggiore o minore di:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

Il primo fallisce perché 9 viene dopo 11 se ordinato lessicograficamente.

Si noti che l'uso delle virgolette non determina se si stanno confrontando stringhe o numeri, come fa l'operatore. È possibile aggiungere o rimuovere le virgolette sopra, non fa alcuna differenza. Bash rileva variabili indefinite tra parentesi doppie, quindi le virgolette non sono necessarie. L'uso delle virgolette con parentesi singole per i test numerici non ti salverà dal momento che:

[ "" -lt 11 ]

è comunque un errore ("espressione intera richiesta"). Le virgolette sono una protezione efficace con confronti di stringhe tra parentesi singole:

[ "" \< 11 ]

Nota tra parentesi doppie , ""sarà -eq 0ma non == 0.


1
In bash, non è strettamente necessario citare le variabili tra parentesi doppie: il builtin [[è abbastanza intelligente da ricordare dove si trovano le variabili e non verrà ingannato da variabili vuote. Le parentesi singole ( [) non dispongono di questa funzione e richiedono virgolette.
Glenn Jackman,

@glennjackman Non l'aveva notato. [[ -lt 11 ]]è un errore, ma nothing=; [[ $nothing -lt 11 ]]non lo è. Ho rielaborato un po 'l'ultimo paragrafo.
Riccioli d'oro,

2

Oltre a quanto detto.
Il confronto per l'uguaglianza è più veloce con i numeri, anche se negli script di shell è raro che sia necessario un calcolo rapido.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s

Considerando che fanno cose diverse, direi che la performance è irrilevante: devi usare quella che fa quello che vuoi.
godlygeek,

@godlygeek Il confronto di uguaglianza di una variabile può essere ottenuto in entrambi i modi. "-eq" è più veloce.
Emmanuel,

Testano diverse definizioni di uguaglianza. Se vuoi rispondere alla domanda "Questa variabile contiene esattamente la stringa 123", puoi usare solo =, poiché l'uso -eqcorrisponderebbe anche a "+123". Se volessi sapere "Questa variabile, se valutata come espressione aritmetica, confronta uguale a 123", potresti usare solo -eq. L'unica volta in cui riesco a capire dove un programmatore non si preoccuperebbe di quale definizione di uguaglianza è stata usata è quando sa che i contenuti della variabile sono vincolati in anticipo a un modello particolare.
godlygeek,

@godlygeek interessante, la domanda era sul confronto dell'uguaglianza dei numeri come se fossero stringhe, si adatta al caso di variabili vincolate in anticipo a un modello particolare?
Emmanuel,

Il tuo esempio ( b=234) si adatta a quel modello - sai che non è +234 o "234" o "233 + 1", dal momento che lo hai assegnato tu stesso, quindi sai che lo confronta come stringa e come numero sono ugualmente validi. Ma lo script del PO, dato che accetta input come argomento della riga di comando, non ha questo vincolo - considera di chiamarlo come ./param.sh 0+1o./param.sh " 1"
godlygeek
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.