Numerisk jämförelse i Bash

Här diskuteras programmering och utveckling
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

Har ett litet problem här. Håller på med ett litet skript här och några rader i skriptet ägnar sig lite åt villkor med numeriska värden inblandade. Värdena slumpas fram och sparas i x så att 000000 ≤ x ≤ 999999.
Nu vill jag dock utesluta alla nummer som startar med 0, så:

Kod: Markera allt

if (( x < 10**5 )); then
	#gör någonting
else
	#gör någonting annat
fi
Problemet tycks vara att tal som startar med 0 ses som oktala och om en siffra > 7 ingår där (ej tillåtet i oktala tal) så blir det ett felmeddelande istället.

Finns något enkelt sätt att kringgå detta, eller måste jag göra en strängjämförelse istället?
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

I och för sig, i och med att jag vet att x alltid är 6 tecken långt så kan jag ju kringgå det hela med, som jag nämnde, en strängjämförelse:

Kod: Markera allt

if [[ $x < "100000" ]]; then
	# Gör någonting
else
	# Gör någonting helt annat
fi
Men det skulle ändå vara intressant att få svar på den ursprungliga frågan. Kanske kan vara användbart vid något annat tillfälle…
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
mcNisse
Inlägg: 5211
Blev medlem: 06 feb 2007, 20:51
OS: Debian
Utgåva: Vet inte/ingen utgåva passar

Re: Numerisk jämförelse i Bash

Inlägg av mcNisse »

Det finns ett enkelt sätt filtrera variabler.

${var#mönster} variabeln var filtreras från starten utfrån mönster.
% filtrerar bakifrån och dubbla förekomster filtrerar den längsta matchande strängen.
matchar ingen returneras orginalvärdet.

tex

Kod: Markera allt

x=0002
echo ${x#0*}
==>
2
I exemplet i frågan blir då

Kod: Markera allt

if (( ${x#0*} < 10**5 )); then
   #gör någonting
else
   #gör någonting annat
fi
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

mcNisse skrev:Det finns ett enkelt sätt filtrera variabler.

${var#mönster} variabeln var filtreras från starten utfrån mönster.
% filtrerar bakifrån och dubbla förekomster filtrerar den längsta matchande strängen.
matchar ingen returneras orginalvärdet.

tex

Kod: Markera allt

x=0002
echo ${x#0*}
==>
2
Ah, visst ja, tänkte inte på det. Borde skriva skript lite oftare så att jag blir van vid de olika sätten att göra saker på. Har i alla fall märkt att det går lite lättare numera och jag behöver inte söka upp precis allting, en del sitter faktiskt kvar i minnet. Dock ej just detta, tydligen.

Hur som helst, testade just ditt exempel:

Kod: Markera allt

~$ x=0002
~$ echo ${x#0*}
002
~$
Den tog bara bort en nolla, som synes.

Ska läsa på lite om detta så får vi se om jag kommer fram till en lösning innan jag bestämmer mig för att det är viktigare att sova…
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

Hittade en lösning som fungerar, men tyvärr förstår jag den inte till fullo. Någon som orkar förklara? Ska själv söka efter svaret också, givetvis.

Kod: Markera allt

~$ x=0002
~$ echo ${x##+(0)}
2
~$
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
mcNisse
Inlägg: 5211
Blev medlem: 06 feb 2007, 20:51
OS: Debian
Utgåva: Vet inte/ingen utgåva passar

Re: Numerisk jämförelse i Bash

Inlägg av mcNisse »

Det stämmer bra... Jag testade bara med en inledande 0 :-( Inget bra exempel :-(
Du får lov att använda ## om du vill ta bort flera.
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

Det verkar som att följande också fungerar:

Kod: Markera allt

~$ echo ${x/#+(0)/}
~$ 2
Det vill säga vanlig substitution. Hittade inte denna form när jag letade men fann att följande också bara tar bort en nolla i början av strängen (i slutet om man byter # mot %):

Kod: Markera allt

~$ echo ${x/#0/}
~$ 002
Så det verkar som att +() innebär ”en eller flera av det som står inom parentesen”. Tar jag bort pluset fungerar det inte, tar jag bort parentesen fungerar det inte heller. Jag har dock inte hittat någon manual som säger att det är på detta sättet, utan bara kommit på det med hjälp av diverse experimenterande.
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

mcNisse skrev:Det stämmer bra... Jag testade bara med en inledande 0 :-( Inget bra exempel :-(
Du får lov att använda ## om du vill ta bort flera.
Det verkar inte heller fungera:

Kod: Markera allt

~$ x=0002
~$ echo ${x##0*}

~$
Tvåan försvann också.

Tycker i och för sig att det borde fungera, kanske en bugg?
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
mcNisse
Inlägg: 5211
Blev medlem: 06 feb 2007, 20:51
OS: Debian
Utgåva: Vet inte/ingen utgåva passar

Re: Numerisk jämförelse i Bash

Inlägg av mcNisse »

Jag tänkte inte igenom problemet tillräckligt bra :-[

Kommandot använder sig av glob göra matchningen. Det gör att det inte finns någon sätt att matcha en eller flera tecken i början av en sträng.

Som tur är använder sig bash av extglob. Detta ger ytterligare några varianter att använda sig av:

?(<PATTERN-LIST>) Matches zero or one occurrence of the given patterns
*(<PATTERN-LIST>) Matches zero or more occurrences of the given patterns
+(<PATTERN-LIST>) Matches one or more occurrences of the given patterns
@(<PATTERN-LIST>) Matches one of the given patterns
!(<PATTERN-LIST>) Matches anything except one of the given patterns

Detta förklarar hur ${x/#+(0)/} fungerar, substituera 1 ellel flera 0 i början av strängen mot tomma strängen.
En annan variant med ## operatorn blir ${x##+(0)}.
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

mcNisse skrev:Jag tänkte inte igenom problemet tillräckligt bra :-[

Kommandot använder sig av glob göra matchningen. Det gör att det inte finns någon sätt att matcha en eller flera tecken i början av en sträng.

Som tur är använder sig bash av extglob. Detta ger ytterligare några varianter att använda sig av:

?(<PATTERN-LIST>) Matches zero or one occurrence of the given patterns
*(<PATTERN-LIST>) Matches zero or more occurrences of the given patterns
+(<PATTERN-LIST>) Matches one or more occurrences of the given patterns
@(<PATTERN-LIST>) Matches one of the given patterns
!(<PATTERN-LIST>) Matches anything except one of the given patterns

Detta förklarar hur ${x/#+(0)/} fungerar, substituera 1 ellel flera 0 i början av strängen mot tomma strängen.
En annan variant med ## operatorn blir ${x##+(0)}.
Ja, det verkar ju bra, allt det där. En sak som jag stör mig lite på är ändå att man prompt måste ha sitt eget sätt att göra det på. Som användare måste man då lära sig flera olika syntaxer för samma sak. Vad kan man göra med ovanstående syntax som man inte kan göra med den syntax som exempelvis sed använder?

Var hittade du detta någonstans förresten? Jag har letat som en idiot efter detta, men inte funnit något läsvärt… Jag söker uppenbarligen efter fel saker…

Tackar för informationen!
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
mcNisse
Inlägg: 5211
Blev medlem: 06 feb 2007, 20:51
OS: Debian
Utgåva: Vet inte/ingen utgåva passar

Re: Numerisk jämförelse i Bash

Inlägg av mcNisse »

glob är enklare en reguljära uttryck.

Här har du länken.
http://wiki.bash-hackers.org/syntax/pattern
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Numerisk jämförelse i Bash

Inlägg av Konservburk »

Johnny Rosenberg skrev:Problemet tycks vara att tal som startar med 0 ses som oktala och om en siffra > 7 ingår där (ej tillåtet i oktala tal) så blir det ett felmeddelande istället.

Finns något enkelt sätt att kringgå detta, eller måste jag göra en strängjämförelse istället?
Du kan explicit ange att talet är decimalt även om det börjar med noll:

Kod: Markera allt

x=089
if (( 10#$x == 89 ))
then echo ja
else echo nej
fi
Användarvisningsbild
Johnny Rosenberg
Inlägg: 1256
Blev medlem: 23 jun 2007, 16:18
OS: Ubuntu
Utgåva: 22.10 Kinetic Kudu
Kontakt:

Re: Numerisk jämförelse i Bash

Inlägg av Johnny Rosenberg »

Konservburk skrev:Du kan explicit ange att talet är decimalt även om det börjar med noll:

Kod: Markera allt

x=089
if (( 10#$x == 89 ))
then echo ja
else echo nej
fi
Oj, det där var något nytt för mig! Har du någon länk där man kan läsa mer om detta?
Vänliga hälsningar

Johnny Rosenberg
ジョニー・ローゼンバーグ

IEEE 1541 - binära prefix
ISO 8601 - datum och tid
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Numerisk jämförelse i Bash

Inlägg av Konservburk »

Johnny Rosenberg skrev:Oj, det där var något nytt för mig! Har du någon länk där man kan läsa mer om detta?
http://www.gnu.org/software/bash/manual ... metic.html

Constants with a leading 0 are interpreted as octal numbers. A leading ‘0x’ or ‘0X’ denotes hexadecimal. Otherwise, numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base. If base# is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, ‘@’, and ‘_’, in that order. If base is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35.
Skriv svar

Återgå till "Programmering och webbdesign"