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

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.

Related

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

Looking to auto increment in a sciprt

I need to increment the package version in the spec file for automating RPM builds.
FILE=somefile1.spec
OLD=$(grep "packageversion 2.64" "$FILE" | awk {'print $3'})
NEW=$(($OLD | bc -l))
echo $NEW
Returns:
change_spec: line 11: 2.64 | bc -l: syntax error: invalid arithmetic operator (error token is ".64 | bc -l")
I am open to suggestions, as you can see, my script writing abilities are nil.
If $OLD equals .64, then you can do:
NEW=$((${OLD:1} + 1))
Just echo $OLD before to see its content (and report it here!).
${OLD:1} simply do a substring, as described here: Substring Extraction.

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"`

Bash Multiplying Decimal to int

I read price from user input. When i multiply the input with int like this
T="$((PRICE*QTY))"|bc; gives
line 272: 12.00: syntax error: invalid arithmetic operator (error token is ".00")
or .50
depending on user input. How do i multiply these two variables and get a total with 2 decimal points?
this works:
PRICE=1.1
QTY=21
RES=$(echo "scale=4; $PRICE*$QTY" | bc)
echo $RES
var=$(echo "scale=2;$PRICE*$QTY" |bc)
You can also use awk
awk -vp=$PRICE -vq=$QTY 'BEGIN{printf "%.2f" ,p * q}'
T="$(echo "$PRICE*$QTY" | bc)"
You can use
mul=0.8
exp=200
texp=awk -vp=$mul -vq=$exp 'BEGIN{printf "%.2f" ,p * q}'
Hope this is going to work.
First, trying to do floating-point arithmetic with bc(1) without using the -l flag is bound to give you some funny answers:
sarnold#haig:~$ bc -q
3.5 * 3.5
12.2
sarnold#haig:~$ bc -q -l
3.5 * 3.5
12.25
Second, the $((...)) is an attempt to do arithmetic in your shell; neither my bash nor dash can handle floating point numbers.
If you want to do the arithmetic in your shell, note printf(1) as well as (probably) your shell's built-in printf function. If you want to do the arithmetic in bc, note the special variable scale.

Resources