You are not logged in.

#1 2010-11-12 20:48:43

ammon
Member
Registered: 2008-12-11
Posts: 413

Noob Bash question

Hi

Im working on this script, something to help me do some stuff quicker.
It reads number from file (that number is only thing in file),

num=`cat file.txt`
Let's say number is in 0.8.

If number is in between 0.2 and 1.4 it launches command, like echo "hi".
And if number is in between 1.4 and 2.8 it lounches other command.

I know how to do case, but only if there is exact number (like 4 or so), not with sequence of numbers.
How to do so?

Last edited by ammon (2010-11-12 20:50:10)

Offline

#2 2010-11-12 21:25:29

frabjous
Member
Registered: 2010-07-13
Posts: 367

Re: Noob Bash question

Originally I was going to answer:

num=`cat file.txt`
if [[ "$num" > "0.2" ]] && [[ "$sum" < "1.4" ]] ; then
     echo "hi"
else
    echo "bye"
fi

But then I remembered BASH doesn't support floating point arithmetic.

Probably the best thing to do would be to strip the decimal point, and add leading/trailing zeros if need be. Supposing that num is always given in the form 'x.y' with a single digit on each side you could just strip the '.':

num=`cat file.txt`
num=${num/./}
if [[ "$num" > "02" ]] && [[ "$sum" < "14" ]] ; then
     echo "hi"
else
    echo "bye"
fi

If num could take any form, or have any number of digits, then it gets more complicated. You might want to google "Floating point arithmetic in BASH", and you'll get a lot of workarounds.

Last edited by frabjous (2010-11-12 21:35:40)

Offline

#3 2010-11-12 21:42:31

Cyrusm
Member
From: Bozeman, MT
Registered: 2007-11-15
Posts: 1,053

Re: Noob Bash question

why don't you use if statements? btw this is more pseudo code than anything.

#!/bin/bash
num=`cat file.txt`
if [[ $num > 0.2 && $num <= 1.4 ]]; then
    #do something
elif [[ $num > 1.4 && $num <= 2.8 ]]; then
    #do something else
else
    #default do something
fi

I think Perl would be better suited for something like this however.

#!/usr/bin/perl
use strict;
use warnings;
use feature 'switch';

open(my $in, "<", "file.txt") or die "Cuidado! Piso Mojado!";

chomp(my $value = <$in>);

given($value){
    when($value > 0.2 && $value <= 1.4)
    {
        print "hey guy!\n";
    }
    when($value > 1.4 && $value <= 2.8)
    {
        system("ls -la"); #or ANY command!
    }
    default
    {
        die "ACHTUNG!";
    }
}
close($in);

  generally, Perl will handle the floating point arithmetic better than bash, and provide you with a much more flexible switch statement.

EDIT: oops, forgot to close my file

Last edited by Cyrusm (2010-11-12 22:31:49)


Hofstadter's Law:
           It always takes longer than you expect, even when you take into account Hofstadter's Law.

Offline

#4 2010-11-12 21:46:19

ammon
Member
Registered: 2008-12-11
Posts: 413

Re: Noob Bash question

Ok, so if I multiply every number with 100. So if there was 0.44 it will be 44.
Then, what would be solution?

Btw, I tried to do it like this. But it does not work, can you tell me why?

num=55
avg=`seq 47 1 79`
norm=`seq 80 1 139`
low=`seq -80 1 39`
case $num in
"$avg" )
echo "ok"
;;
"$norm" )
echo "fine"
;;
"$low" )
echo "lol"
;;
esac

Last edited by ammon (2010-11-12 21:47:25)

Offline

#5 2010-11-12 21:55:02

livibetter
Member
From: Taipei
Registered: 2008-05-14
Posts: 95
Website

Re: Noob Bash question

frabjous wrote:

Originally I was going to answer:

num=`cat file.txt`
if [[ "$num" > "0.2" ]] && [[ "$sum" < "1.4" ]] ; then
     echo "hi"
else
    echo "bye"
fi

But then I remembered BASH doesn't support floating point arithmetic.

That's what I thought when I saw your reply, but I tried few numbers, it seems to work well with string comparison.

However, I would use `bc`:

$ bc <<< "0.5 > 0.2 && 0.5 < 1.4"
1
$ bc <<< "0.0 > 0.2 && 0.0 < 1.4"
0
$ bc <<< "-1.0 > 0.2 && -1.0 < 1.4"
0

Maybe awk is a better option if not really have to use pure Bash script?

Offline

#6 2010-11-12 22:16:22

ammon
Member
Registered: 2008-12-11
Posts: 413

Re: Noob Bash question

Cyrusm wrote:

why don't you use if statements? btw this is more pseudo code than anything.

#!/bin/bash
num=`cat file.txt`
if [[ $num > 0.2 && $num <= 1.4 ]]; then
    #do something
elif [[ $num > 1.4 && $num <= 2.8 ]]; then
    #do something else
else
    #default do something
fi

Yes, that does the trick. Thank you.
But I have problem, it does not work with <=, and number sometimes matches final number (in this case 1.4)
line 4: syntax error in conditional expression
/ line 4: syntax error near `1.4'
line 4: `if [[ $num > 0.2 && $num <= 1.4 ]]; then'

I have to use bash. Ty for perl way, but I can't use it.

Offline

#7 2010-11-12 23:23:18

frabjous
Member
Registered: 2010-07-13
Posts: 367

Re: Noob Bash question

Your idea of multiplying everything by 100 should work, but the question is: how? You can't just multiply "0.4" by 100 in BASH, since BASH can't do any math for strings with decimal points in them.

You can't just remove the decimal point, or you might get wrong results if there aren't exactly 2 decimal places in the value. You don't want it, for example, to think of "1.233" as greater than "10.1".

But you could, e.g., split the num variable into the part before the period, and the point after, and then pad the decimal part with 0s if if were less than 2 characters long, and strip it to two characters if it is more than that.

This feels unnecessarily complicated, but here goes:

# set numwholepart to part of num before .
numwholepart=${num%.*}
# set numdecimalpart to part after .
numdecimalpart=${num#*.}
# if there was no period in the expression
# then you need to make it 00
if [ "$num" == "$numdecimalpart" ] ; then
   numdecimalpart="00"
fi
# count how many digits numdecimalpart has
numdecimalplaces=${#numdecimalpart}
# pad it to the right size
if [ "$numdecimalplaces" == "0" ] ; then
   numdecimalpart="00" # if there was no digits after .
elif [ "$numdecimalplaces" == "1" ] ; then
   numdecimalpart="${numdecimalpart}0" # if there was only 1
else
   numdecimalpart="${numdecimalpart:0:2}" # if there two or more cut it 2
fi
# create combined number by putting whole part before decimal part
numcompare="$numwholepart$numdecimalpart"
# now the number needs to be between 20 and 140
if [ $numcompare -ge 20 ] && [ $numcompare -lt 140 ] ; then
    echo "yes"
else
    echo "no"
fi

Offline

#8 2010-11-13 00:00:11

falconindy
Developer
From: New York, USA
Registered: 2009-10-22
Posts: 4,111
Website

Re: Noob Bash question

ammon wrote:
Cyrusm wrote:

why don't you use if statements? btw this is more pseudo code than anything.

#!/bin/bash
num=`cat file.txt`
if [[ $num > 0.2 && $num <= 1.4 ]]; then
    #do something
elif [[ $num > 1.4 && $num <= 2.8 ]]; then
    #do something else
else
    #default do something
fi

Yes, that does the trick. Thank you.
But I have problem, it does not work with <=, and number sometimes matches final number (in this case 1.4)
line 4: syntax error in conditional expression
/ line 4: syntax error near `1.4'
line 4: `if [[ $num > 0.2 && $num <= 1.4 ]]; then'

I have to use bash. Ty for perl way, but I can't use it.

No. This does not work. Bash cannot compare floating point numbers. You are getting false positives as a result of a lexicographical comparison.

[[ 2 > 10 ]] && echo yes

Offline

#9 2010-11-13 06:14:50

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 859
Website

Re: Noob Bash question

man expr people!

if [ $(expr 0.5 '>' 0.2) -eq 1 ]; then
  echo greater
fi

Last edited by tavianator (2010-11-13 06:15:01)

Offline

#10 2010-11-13 06:27:54

frabjous
Member
Registered: 2010-07-13
Posts: 367

Re: Noob Bash question

tavianator wrote:

man expr people!

No, expr does not handle floating point arithmetic properly.

$ if [ $(expr 20.0 '>' 100.0) -eq 1 ]; then echo greater; fi
greater

Since when is 20 greater than 100? It isn't. It just comes after it alphabetically.

Last edited by frabjous (2010-11-13 06:53:10)

Offline

#11 2010-11-13 07:43:33

lolilolicon
Member
Registered: 2009-03-05
Posts: 1,722

Re: Noob Bash question

@ammon I don't know what you mean by "with sequence of numbers"... maybe you need a while loop? Please post a sample file for clarification.

Anyway, I think I've found a way to do float number comparision with pure bash, by compare the int part and float part separately:

#!/bin/bash

cmp_float()
{
  local p a b a_l a_r b_l b_r
  p=${3:-2} # precision
  a=$(printf %.${p}f $1)
  b=$(printf %.${p}f $2)

  a_l=${a%.*}; a_r=${a#*.}
  b_l=${b%.*}; b_r=${b#*.}

  if (($a_l == $b_l)); then
    cmp $a_r $b_r
  else
    cmp $a_l $b_l
  fi
}

cmp()
{
  if (($1 == $2)); then
    echo 0
  elif (($1 > $2)); then
    echo 1
  elif (($1 < $2)); then
    echo -1
  fi
}

cmp_float $1 $2

Edit:
Oops, didn't see post #7. Haha, gr8t mindz th1nk 'like.

Last edited by lolilolicon (2010-11-13 07:51:22)


This silver ladybug at line 28...

Offline

#12 2010-11-13 18:05:01

tavianator
Member
From: Waterloo, ON, Canada
Registered: 2007-08-21
Posts: 859
Website

Re: Noob Bash question

frabjous wrote:
tavianator wrote:

man expr people!

No, expr does not handle floating point arithmetic properly.

$ if [ $(expr 20.0 '>' 100.0) -eq 1 ]; then echo greater; fi
greater

Since when is 20 greater than 100? It isn't. It just comes after it alphabetically.

Huh, well I feel silly.  I'd go with bc then, or use zsh (which does support floating point arithmetic).

Offline

#13 2010-11-13 21:49:01

xl666
Member
From: México
Registered: 2010-11-12
Posts: 7

Re: Noob Bash question

I think that in scenarios like this script languages like python or pearl are more suitable.

Offline

#14 2010-11-16 15:05:53

quigybo
Member
Registered: 2009-01-15
Posts: 223

Re: Noob Bash question

I guess awk would be the most common tool to reach for at this point:

gt_awk() { awk "BEGIN { exit ($1 > $2) ? 0 : 1 }" ;}
xl666 wrote:

I think that in scenarios like this script languages like python or pearl are more suitable.

May I recommend lua? It is amazingly small and fast, so for things like this it is ideal:

gt_lua() { lua -e "os.exit( ($1 > $2) and 0 or 1 )" ;}

If you want to take it one step further and write the whole thing in lua, the io module for reading the file and os.execute() to call external commands will be all you need. wink

Just for curiosity's sake:

gt_perl() { perl -e "exit (($1 > $2) ? 0 : 1)" ;}

And for completeness (be careful that here 0 is false and 1 is true, unlike bash exit codes):

gt_bc() { bc <<< "$1 > $2" ;}

An amazingly oversimplified comparison:

$ time for i in {1..1000}; do gt_awk 0.8 1.4 >/dev/null; done

real    0m5.571s
user    0m2.873s
sys    0m0.787s
$ time for i in {1..1000}; do gt_perl 0.8 1.4 >/dev/null; done

real    0m3.237s
user    0m0.347s
sys    0m0.237s
$ time for i in {1..1000}; do gt_bc 0.8 1.4 >/dev/null; done

real    0m3.027s
user    0m0.200s
sys    0m0.247s
$ time for i in {1..1000}; do gt_lua 0.8 1.4 >/dev/null; done

real    0m1.594s
user    0m0.133s
sys    0m0.183s

A large part of this time would be starting the interpreter, but for the small script the OP needs it is still significant. Of course perl and lua can read the file themselves, thus the entire script could be in those languages.

BTW: If you google "bash floating point" you should come up with enough results to figure out how you could solve this.

Offline

#15 2010-11-16 22:01:12

frabjous
Member
Registered: 2010-07-13
Posts: 367

Re: Noob Bash question

That's a nice plug for Lua, quigybo. I've been thinking about which of several languages to learn next, and while this seems like a very small and tiny consideration, somehow it does push me a little more towards Lua (which I had been considering anyway, for other reasons).

Out of curiousity, I tried Python for comparison:

$ gt_python() { python -c "($a > $b) or exit(1)" ;}
$ time for i in {1..1000}; do gt_python 0.8 1.4 >/dev/null; done

real    0m26.838s
user    0m18.192s
sys    0m5.580s

Ouch! That took forever.

For comparison, here are my values for the other functions. bc was actually faster than lua, and perl did quite well too.

$ time for i in {1..1000}; do gt_bc 0.8 1.4 >/dev/null; done

real    0m1.102s
user    0m0.060s
sys    0m0.147s

$ time for i in {1..1000}; do gt_lua 0.8 1.4 >/dev/null; done

real    0m1.171s
user    0m0.080s
sys    0m0.153s

$ time for i in {1..1000}; do gt_perl 0.8 1.4 >/dev/null; done

real    0m2.053s
user    0m0.070s
sys    0m0.187s

$ time for i in {1..1000}; do gt_awk 0.8 1.4 >/dev/null; done

real    0m3.608s
user    0m1.723s
sys    0m0.587s

Last edited by frabjous (2010-11-16 22:01:33)

Offline

#16 2010-11-17 19:14:55

ammon
Member
Registered: 2008-12-11
Posts: 413

Re: Noob Bash question

Guys, thanks for the replies.
But I will need further help. I don't understand half of this you wrote.
Ok, bash is outta question, but I can use zsh and python.
Actually I was working in zsh. But I did not mention it becouse I tought it is not important.

So, what I need is:
I have list of numbers, so it is a file with list of numbers.
It is not problem to read from it. Ok, lets move on.
Number is readed and compared to designated values,
I have this "border values", like first is:
0.8 and 2.0 (numbers can be $num >= 0.8),
-0.38 to 0.38 ($num can also be >= 0.38)

I have 5 such cases. And for each I have command that louches something after considering where number belongs.
So...
if $num > 0.8
cat /etc/blabla > file.txt
if $num >= -0.39 to =< 0.39
cat /etc/blabla1 > file.txt

P.S.
Someone mentioned awk. How to do this with awk?

Last edited by ammon (2010-11-17 22:17:44)

Offline

Board footer

Powered by FluxBB