2012-02-19 22 views
6

Non sono addestrato in Linux, ma posso cavarmela con alcune ricerche di documenti, ma sono perplesso.Shell: "a -lt b" non significa vero se a è minore di b?

Ho trovato uno script che consente di impostare la data sul mio router dd wrt all'avvio, ma solo se la data corrente è inferiore alla data memorizzata. Posso condividere l'intero script, se lo desideri, ma si riduce a questa affermazione che non si valuta come vera quando mi aspetto che lo faccia. Sto mettendo i letterali, piuttosto che le variabili, e ancora non restituire true, viene eseguito il "resto" dichiarazione:

if [ 021715402012 -lt 021815402012 ] 
    then 
    echo "the first seems less than the second" 
    else 
    echo "the first does not seem less than the second for some reason" 
    fi 

mi si aspetterebbe "il primo sembra inferiore al secondo", ma questo non è il caso ... È un problema di overflow? Ho provato a fare una stringa confrontare come questo:

if [ x021715402012 -lt x021815402012 ] 

e provato a mettere tra virgolette:

if [ "x021715402012" -lt "x021815402012" ] 

esegue sempre l'altro. "a -lt b" non significa vero se a è minore di b?

Qualsiasi comprensione di questo sarebbe apprezzato, sono perplesso!

+0

Funziona bene in 'bash' sul mio Mac. – kindall

+1

Hmm stampa "il primo sembra meno del secondo" per me quando provo il tuo esempio. Che versione bash o sh è questa? Se vuoi maggiori informazioni su -lt ecc. Puoi controllare 'help test',' help ['o' man test'. –

+0

Funziona anche bene sul mio router openwrt. Ma ho chiaramente un'implementazione diversa, perché la mia dà "brutto numero" con "x" incluso. –

risposta

3

È possibile che i mnemonici come -lt provengano dai comparatori originali di Fortran come .LT. della fine degli anni '50.

Sì, nella shell, -lt esegue un confronto numerico "minore di". (Si noti, tuttavia, che in Perl i confronti numerici sono < e che i confronti tra stringhe sono indicati dagli operatori alfabetici come -lt!)

Tuttavia, in alcune shell, forse molte, la conversione e il confronto potrebbero essere eseguiti nel formato intero locale lungo. Se ci si trova su una macchina a 32 bit, i valori citati superano l'intervallo di 32 bit (firmato) di un fattore pari o superiore a 10. Su una macchina a 64 bit, o con una shell che utilizza long long, starai bene.

Gli equivalenti esadecimali dei numeri decimali sono 021715402012 = 0x50E56BD1C e 021815402012 = 0x5144C9E1C; non possono essere ottali a causa dell'8. (Tuttavia, se la shell interpreta lo zero iniziale come "ottale", il secondo numero è solo 021 o 17 decimale perché l'8 termina il numero ottale. conchiglie bit ho testati su (Mac OS X 10.7.3 e RHEL 5) entrambi sembravano trattarli come decimali, non ottale)

il codice di esempio riportato di seguito, compilato sotto a 64 bit ha pronunciato la seguente uscita:.

021715402012 = 240565532 = 0x050E56BD1C 
021815402012 = 340565532 = 0x05144C9E1C 

compilato sotto a 32 bit, che ha pronunciato la seguente uscita:

021715402012 = 2147483647 = 0x007FFFFFFF 
021815402012 = 2147483647 = 0x007FFFFFFF 

Se questo è stato cosa è successo nella tua shell, quindi il comportamento risultante di -lt verrà spiegato. È possibile confermarlo testando se i due valori sono -eq; in modo intuitivo, questo probabilmente verrebbe valutato su true nell'ipotesi che si stia utilizzando una shell a 32 bit che limita la sua aritmetica agli interi con segno long (32 bit).

#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    char *array[] = { "021715402012", "021815402012" }; 
    for (int i = 0; i < 2; i++) 
    { 
     int j = atoi(array[i]); 
     long k = strtol(array[i], 0, 10); 
     printf("%-10s = %10d = 0x%.10lX\n", array[i], j, k); 
    } 
    return 0; 
} 
+0

Grazie a @ Jonathan per una risposta così completa. Credo che il problema sia la condizione di overflow, sono semplicemente entrambi troppo grandi. Ho rimosso il 2012 alla fine di ciascuno e ha iniziato a comportarsi come previsto, anche con lo zero iniziale. Quindi la teoria del confronto ottale non era il caso qui. Dato che questi numeri sono in realtà date in uno strano ordine di caratteri (mmddHHMMyyyy), dividerò il confronto per confrontare gli anni prima con il mmddHHMM, e credo che risolverà questo problema. Grazie ancora! –

1

Quale shell e versione stai usando? Qualche possibilità di un personaggio di controllo incorporato da qualche parte? (Provare a eliminare ri-digitare il codice.) Su openSUSE 11.2 (x86_64):

$if [ 021715402012 -lt 021815402012 ]; then echo yes; else echo no; fi 
yes 

È interessante notare, però,

$if [ 012 -lt 11 ]; then echo yes; else echo no; fi 
no 

Questo mi sorprende perché man bash dice che -lt esegue un confronto aritmetico, e un costante con uno 0 iniziale viene interpretato come ottale. Quindi mi aspetto che questo test per verificare se dieci è inferiore a undici, perché 012 base 8 = 8 + 2.

Qualcuno può metterci in fila?

+0

Interessante trovare lì. – hochl

+0

La tua shell non sta interpretando l'ottale .... e forse lo è anche Brian. –

+1

Anche io ho "no". Il problema era come detto sopra @ Jonathan - i numeri erano troppo grandi e quindi, presumo, interpretati come gli stessi. Ho confermato questo cambiando -lt in -gt. Diceva ancora "no" se non gt e non lt, deve essere uguale, quindi ... agli occhi di un piccolo router. –

0

io non sono sicuro se il vostro particolare shell sta usando questa interpretazione, ma qui è quello che penso sta accadendo:

021715402012 è un numero ottale a 11 cifre. 021815402012 è un numero ottale a due cifre terminato da una cifra non ottale (8).

di 021715402012 e 021, chiaramente il secondo è più piccolo.

Per quanto riguarda gli altri tentativi, la documentazione per i comandi test e [ indica che -lt è valido solo per gli argomenti numerici, non stringhe.

+0

ipotesi interessante; il test dell'acido fa cadere lo zero iniziale. La versione 'x' sarà uguale, non è così, perché entrambe le stringhe valutano a zero un valore numerico. –

+0

@ JonathanLeffler: Probabilmente. Fammi vedere se la mia casella openwrt può riprodurre il comportamento originale ....in realtà ottengo il comportamento "previsto" con gli zeri iniziali e con "x" ottengo "ash: x021715402012: numero errato". –

1

Un paio di cose stanno accadendo ...

vostro guscio sembra essere utilizzando 32 bit aritmetica e i numeri sono traboccante il formato.

Inoltre, tutti i parametri di comando negli script di shell sono stringhe, quindi le virgolette non avranno alcun effetto a meno che i caratteri in un parametro non siano significativi per l'analizzatore di shell.

L'istruzione shell if è attualmente in esecuzione the test(1) command che è collegata a [ come una scorciatoia. (E poi ignora un finale ]). Sebbene il comando avrebbe potuto utilizzare lo stesso operatore per il confronto numerico e di stringa, il comando viene progettato in modo tale che gli operatori assumano un determinato tipo e -lt assume valori numerici.

Su bash e cenere (trattino) Viene visualizzato un messaggio di errore nell'esempio x ....

Problemi correlati