bash float comparison and (standard_in) 1: syntax error - bash

I have this piece of code where I am getting (standard_in) 1: syntax error from the third line:
valueInFloat=printf "%.2g" $temp
tempFloat=printf "%.1f" $value2
compare_result=`echo "$tempFloat < $valueInFloat" |bc -l`
if [[ -z $compare_result ]]
then ...
I am trying to do float value comparison in bash.
Note that, if I comment out the third line and put compare_result=0, the error goes away.

There are situations in which printf can generate floating point values that bc won't recognise. Specifically, something like:
pax> printf "%.2g\n" 42456456457357357
4.2e+16
pax> echo '4.2e+16 > 1.0' | bc -l
(standard_in) 1: syntax error
I suggest you stick with the %f variant. It will always generate the form [−]999.999 as per the ISO C standard, which bc will have no trouble with (unless you start getting into infinities or NaNs). The %g variant generates either that format or the %e format [−]9.999e±99 depending on the value and precision requested.
In addition, your test is wrong. The -z test will be true if the string is empty, and your string will either be 1 or 0 depending on the result of the comparison. A better test would be (assuming you wanted to rest if the test was positive:
if [[ ${compare_result} -eq 1 ]]

Either of $tempFloat or $valueInFloat has no value.

Related

Addition/subtraktion/multiplikation of floats in bash with bc -l

I have some trouble with such an easy task...
Please find relevant code below:
loewdin_fuk=$(echo $line_fukui|awk '{print $4}')
nbo_fuk=$(echo $line_fukui|awk '{print $5}')
echo "loewdin_fuk $loewdin_fuk nbo_fuk $nbo_fuk"
aver_fuk=$(($loewdin_fuk + $nbo_fuk))
\#aver_fuk=$(echo "scale=4; 0.5*($loewdin_fuk $nbo_fuk)" | bc -l)
The output is:
loewdin_fuk +0.1662 nbo_fuk +0.1865
./collectFukui.sh: line 151: +0.1662 + +0.1865: syntax error: invalid
arithmetic operator (error token is ".1662 + +0.1865")
Using the command line:
aver_fuk=$(echo "scale=4; 0.5*($loewdin_fuk $nbo_fuk)" | bc -l)
leads to following output:
loewdin_fuk +0.1662 nbo_fuk +0.1865
(standard_in) 1: syntax error
I don't get what's wrong... Thank you in advance!
Best,
Me
The problem here is that bc does not consider + to be a unary operator. So +0.1662 +0.1865 is invalid syntax. (It would have worked ok if the first number were negative, because - is a unary operator.)
So if you want to use bc, you need to do something like:
aver_fuk=$(echo "scale=4; 0.5*(0$loewdin_fuk $nbo_fuk)" | bc -l)
Prepending 0 without a space will work whether or not $loewdin_fuk starts with a sign character. If you put a space in between, it would work with values with explicit sign characters, but fail on values without a sign.

How to turn a function output, into a number [duplicate]

This question already has answers here:
How can I compare two floating point numbers in Bash?
(22 answers)
Floating point comparison in shell
(7 answers)
Closed 4 years ago.
So want to turn this function, whose output is a number. The number is curled through some json file thats irrelevant.
#error input 1
if (($(masterfunc) >= 1)); then
#I've also tried
#error input 2
if (($(masterfunc | bc -l) >= 1)); then
I get these this error, which I'm assuming is because its outputing it as a letter or command and not as a number.
#error output 1
((: 1.00048333447157914468 >= 1: syntax error: invalid arithmetic
operator (error token is ".00048333447157914468 >= 1")
#error output 2
((: .99989817794934530799 >= 1: syntax error: operand expected (error
token is ".99989817794934530799 >= 1")
I'm assuming this is some floating point arithmetic problem, but then it should of been solved through bc?
I'm new to bash so if the problem is some unrelated syntax error I apologize.
This is actually rather complicated. The shell doesn't understand real numbers at all, so you have to get something else (like bc) to do the comparison and output something simpler that bash can understand. The simplest way I see to do this is:
if [ $(echo "$(masterfunc) >= 1" | bc) -eq 1 ]; then
Explanation, step by step:
echo "$(masterfunc) >= 1" runs the masterfunc function, adds ">= 1" to its output, and sends the result (something like "1.00048333447157914468 >= 1") to standard output.
echo "$(masterfunc) >= 1" | bc runs the above and pipes it to bc, which will do the comparison and print "1" if the masterfunc output is greater than or equal to 1, "0" if it's less. Note: you can try running this by hand and see how it works.
This "1"/"0" output is more along the lines of what bash can understand, but we still need to actually tell bash what to make of it.
[ $(echo "$(masterfunc) >= 1" | bc) -eq 1 ] runs the above, captures its output with $( ), and embeds that in a test expression. Basically, depending on the output from bc, this is equivalent to either [ 1 -eq 1 ] or [ 0 -eq 1 ].
Use this:
if (( $(printf '%s >= 1\n' "$(masterfunc)" | bc -l) )); then ...

Napierian logarithm with BASH

I would like to compute a Napierian logarithm with Bash.
Here is my code using what I found in the internet. The values I use are in output files.
for nn in 1 2 3 4 ; do
for w in 0.5 ; do
a=`grep ' # Final variational ensemble-DFT energy: ' $nn"_"$w.out`
for n in 1000 ; do
b=`grep ' # Final variational ensemble-DFT energy: ' $n"_"$w.out`
done
done
c=`echo "l({$a - $b})" | bc -l`
d=`echo "l({$nn})" | bc -l`
echo $d" "$c >> data_slope.dat
done
I want to plot $d = f($c) from data_slope.dat, but apparently this notation with bc -l doesn't work for me.
I obtain this kind of error :
(standard_in) 1: illegal character: #
(standard_in) 1: syntax error
(standard_in) 1: illegal character: T
(standard_in) 1: syntax error
(standard_in) 1: illegal character: :
(standard_in) 1: syntax error
Whereas I've already done some bash code like this without trying to compute a logarithm, and I didn't get those errors.
The syntax errors are reported by bc. Its syntax for subtraction expects numbers on both sides of the - operand, not strings containing #. The curly braces are wrong, too: you can experiment with
a=3
b=0.2
for expression in "l($a-$b)" "l({$a-$b})" "({#F - #T})" ; do
echo "$expression"
echo "$expression" | bc -l
done
Thanks to your comments I manage to find my mistake.
#Etan Reisner : Indeed my a and b are wrong, I did not grep the values of a and b but the sentence before the values... I should have put ${a:47:16} instead of $a to grep the value of a, which is a real number. Same for $b.
#choroba : I removed the curly braces, now it works.

confirm numeric character of variable

I need to do an operation but something is wrong in my code in bash
I have 4 variables, km1, km2, km3, km4.
I want to sum the 4 variables except when the value is "CLOSED"
3.200
CLOSED
1.800
0.600
When I do the following sum, there is an error...I thing my variables are not numeric, any help? How can I force them to be numeric and then do the sum?
let km=$km1+$km3+$km4
echo $km
./sum.sh: line 41: let: km=3.200: syntax error: invalid arithmetic operator (error token is ".200")
km1=3.200
km2=CLOSED
km3=1.800
km4=0.600
total=`LC_ALL=C echo "$km1 $km2 $km3 $km4"|awk '{sum += $1+$2+$3+$4}END {print sum}'`
Not that good with awk but i think the above can help. total the is sum of all vars
There are 2 issues with you code. The first one is that you are trying to work with values other than integers. Bash only does integers. You can round up the values to integers using bc (An arbitrary precision calculator language). The second issue is that you are trying to do math on strings. So consider the code below:
#!/bin/bash
km1=3.200;
km2="CLOSED";
km3=1.800;
km4=0.600;
km1=$(echo "$km1/1" | bc)
km3=$(echo "$km3/1" | bc)
km4=$(echo "$km4/1" | bc)
array=($km1 $km2 $km3 $km4)
for i in ${array[#]}; do
case $i in
*[0-9]*)
(( result+=$i ))
esac
done
echo $result

Convert string into integer in bash script - "Leading Zero" number error

In a text file, test.txt, I have the next information:
sl-gs5 desconnected Wed Oct 10 08:00:01 EDT 2012 1001
I want to extract the hour of the event by the next command line:
hour=$(grep -n sl-gs5 test.txt | tail -1 | cut -d' ' -f6 | awk -F ":" '{print $1}')
and I got "08". When I try to add 1,
14 echo $((hour+1))
I receive the next error message:
./test2.sh: line 14: 08: value too great for base (error token is "08")
If variables in Bash are untyped, why?
See ARITHMETIC EVALUATION in man bash:
Constants with a leading 0 are interpreted as octal numbers.
You can remove the leading zero by parameter expansion:
hour=${hour#0}
or force base-10 interpretation:
$((10#$hour + 1))
what I'd call a hack, but given that you're only processing hour values, you can do
hour=08
echo $(( ${hour#0} +1 ))
9
hour=10
echo $(( ${hour#0} +1))
11
with little risk.
IHTH.
You could also use bc
hour=8
result=$(echo "$hour + 1" | bc)
echo $result
9
Here's an easy way, albeit not the prettiest way to get an int value for a string.
hour=`expr $hour + 0`
Example
bash-3.2$ hour="08"
bash-3.2$ hour=`expr $hour + 0`
bash-3.2$ echo $hour
8
In Short: In order to deal with "Leading Zero" numbers (any 0 digit that comes before the first non-zero) in bash
- Use bc An arbitrary precision calculator language
Example:
a="000001"
b=$(echo $a | bc)
echo $b
Output: 1
From Bash manual:
"bc is a language that supports arbitrary precision numbers with interactive execution
of statements. There are some similarities in the syntax to the C programming lan-
guage. A standard math library is available by command line option. If requested, the
math library is defined before processing any files. bc starts by processing code from
all the files listed on the command line in the order listed. After all files have
been processed, bc reads from the standard input. All code is executed as it is read.
(If a file contains a command to halt the processor, bc will never read from the standard input.)"
Since hours are always positive, and always 2 digits, you can set a 1 in front of it and subtract 100:
echo $((1$hour+1-100))
which is equivalent to
echo $((1$hour-99))
Be sure to comment such gymnastics. :)
The leading 0 is leading to bash trying to interpret your number as an octal number, but octal numbers are 0-7, and 8 is thus an invalid token.
If I were you, I would add some logic to remove a leading 0, add one, and re-add the leading 0 if the result is < 10.
How about sed?
hour=`echo $hour|sed -e "s/^0*//g"`

Resources