I'm simply trying to multiplying some float variables using bc:
#!/bin/bash
a=2.77 | bc
b=2.0 | bc
for cc in $(seq 0. 0.001 0.02)
do
c=${cc} | bc
d=$((a * b * c)) | bc
echo "$d" | bc
done
And this does not give me an output. I know it's a silly one but I've tried a number of combinations of bc (piping it in different places etc.) to no avail.
Any help would be greatly appreciated!
bc is a command-line utility, not some obscure part of shell syntax. The utility reads mathematical expressions from its standard input and prints values to its standard output. Since it is not part of the shell, it has no access to shell variables.
The shell pipe operator (|) connects the standard output of one shell command to the standard input of another shell command. For example, you could send an expression to bc by using the echo utility on the left-hand side of a pipe:
echo 2+2 | bc
This will print 4, since there is no more here than meets the eye.
So I suppose you wanted to do this:
a=2.77
b=2.0
for c in $(seq 0. 0.001 0.02); do
echo "$a * $b * $c" | bc
done
Note: The expansion of the shell variables is happening when the shell processes the argument to echo, as you could verify by leaving off the bc:
a=2.77
b=2.0
for c in $(seq 0. 0.001 0.02); do
echo -n "$a * $b * $c" =
echo "$a * $b * $c" | bc
done
So bc just sees numbers.
If you wanted to save the output of bc in a variable instead of sending it to standard output (i.e. the console), you could do so with normal command substitution syntax:
a=2.77
b=2.0
for c in $(seq 0. 0.001 0.02); do
d=$(echo "$a * $b * $c" | bc)
echo "$d"
done
To multiply two numbers directly, you would do something like:
echo 2.77 * 2.0 | bc
It will produce a result to 2 places - the largest number of places of the factors. To get it to a larger number of places, like 5, would require:
echo "scale = 5; 2.77 * 2.0" | bc
This becomes more important if you're multiplying numerals that each have a large number of decimal places.
As stated in other replies, bc is not a part of bash, but is a command run by bash. So, you're actually sending input directly to the command - which is why you need echo. If you put it in a file (named, say, "a") then you'd run "bc < a". Or, you can put the input directly in the shell script and have a command run the designated segment as its input; like this:
cat <<EOF
Input
EOF
... with qualifiers (e.g. you need to write "" as "\", for instance).
Control flow constructs may be more problematic to run in BC off the command line. I tried the following
echo "scale = 6; a = 2.77; b = 2.0; define f(cc) { auto c, d; c = cc; d = a*b*c; return d; } f(0); f(0.001); f(0.02)" | bc
and got a syntax error (I have a version of GNU-BC installed). On the other hand, it will run fine with C-BC
echo "scale = 6; a = 2.77; b = 2.0; define f(cc) { auto c, d; c = cc; d = a * b * c; return d; } f(0); f(0.001); f(0.02)" | cbc
and give you the expected result - matching the example you cited ... listing numbers to 6 places.
C-BC is here (it's operationally a large superset of GNU-BC and UNIX BC, but not 100% POSIX compliant):
https://github.com/RockBrentwood/CBC
The syntax is closer to C, so you could also write it as
echo "scale = 6, a = 2.77, b = 2.0; define f(cc) { return a * b * cc; } f(0); f(0.001); f(0.02)" | cbc
to get the same result. So, as another example, this
echo "scale = 100; for (x = 0, y = 1; x < 50; y *= ++x); y" | cbc
will give you 50 factorial. However, comma-expressions, like (x = 0, y = 1) are not mandated for bc by POSIX, so it will not run in other bc dialects, like GNU BC.
Related
I'm having a syntax error ((standard_in): syntax error)on 3rd and 5th line.
#!/bin/bash
i=`echo "8.8007751822"|bc`
rws = `echo "0.49237251092*$i" |bc`
rmt = `echo "0.85 * $rws"| bc`
dx = `echo "log ($rws / 0.000001) / 720.0" | bc`;
Can anyone help me?
A few things:
Assignments must not have blanks around the =
i=`echo "8.8007751822"|bc` is a really complicated way to write i=8.8007751822
bc has no function log, there's only l for the natural logarithm (and l requires the -l option to be enabled)
I would move everything into bc instead of calling it multiple times:
bc -l <<'EOF'
i = 8.8007751822
rws = i * 0.49237251092
rmt = 0.85 * rws
dx = (l(rws / 0.000001) / l(10)) / 720
dx
EOF
This prints the value of dx.
I am a new bash learner. I want to print the result of an expression given as input having 3 digits after decimal point with rounding if needed.
I can use the following code, but it does not round. Say if I give 5+50*3/20 + (19*2)/7 as input for the following code, the given output is 17.928. Actual result is 17.92857.... So, it is truncating instead of rounding. I want to round it, that means the output should be 17.929. My code:
read a
echo "scale = 3; $a" | bc -l
Equivalent C++ code can be(in main function):
float a = 5+50*3.0/20.0 + (19*2.0)/7.0;
cout<<setprecision(3)<<fixed<<a<<endl;
What about
a=`echo "5+50*3/20 + (19*2)/7" | bc -l`
a_rounded=`printf "%.3f" $a`
echo "a = $a"
echo "a_rounded = $a_rounded"
which outputs
a = 17.92857142857142857142
a_rounded = 17.929
?
You can use awk:
awk 'BEGIN{printf "%.3f\n", (5+50*3/20 + (19*2)/7)}'
17.929
%.3f output format will round up the number to 3 decimal points.
Try using this:
Here bc will provide the bash the functionality of caluculator and -l will read every single one in string and finally we are printing only three decimals at end
read num
echo $num | bc -l | xargs printf "%.3f"
I do something like the following in a Makefile:
echo "0.1 + 0.1" | bc
(in the real file the numbers are dynamic, of course)
It prints .2 but I want it to print 0.2.
I would like to do this without resorting to sed but I can't seem to find how to get bc to print the zero. Or is bc just not able to do this?
You can also resort to awk to format:
echo "0.1 + 0.1" | bc | awk '{printf "%f", $0}'
or with awk itself doing the math:
echo "0.1 0.1" | awk '{printf "%f", $1 + $2}'
This might work for you:
echo "x=0.1 + 0.1; if(x<1) print 0; x" | bc
After a quick look at the source (see bc_out_num(), line 1461), I don't see an obvious way to make the leading 0 get printed if the integer portion is 0. Unless I missed something, this behaviour is not dependent on a parameter which can be changed using command-line flag.
Short answer: no, I don't think there's a way to make bc print numbers the way you want.
I don't see anything wrong with using sed if you still want to use bc. The following doesn't look that ghastly, IMHO:
[me#home]$ echo "0.1 + 0.1" | bc | sed 's/^\./0./'
0.2
If you really want to avoid sed, both eljunior's and choroba's suggestions are pretty neat, but they require value-dependent tweaking to avoid trailing zeros. That may or may not be an issue for you.
I cannot find anything about output format in the documentation. Instead of sed, you can also reach for printf:
printf '%3.1f\n' $(bc<<<0.1+0.1)
echo "$a / $b" | bc -l | sed -e 's/^-\./-0./' -e 's/^\./0./'
This should work for all cases where the results are:
"-.123"
".123"
"-1.23"
"1.23"
Explanation:
For everything that only starts with -., replace -. with -0.
For everything that only starts with ., replace . with 0.
Building on potongs answer,
For fractional results:
echo "x=0.1 + 0.1; if(x<1 && x > 0) print 0; x" | bc -l
Note that negative results will not be displayed correctly. Aquarius Power has a solution for that.
$ bc -l <<< 'x=-1/2; if (length (x) == scale (x) && x != 0) { if (x < 0) print "-",0,-x else print 0,x } else print x'
This one is pure bc. It detects the leading zero by comparing the result of the length with the scale of the expression. It works on both positive and negative number.
This one will also handle negative numbers:
echo "0.1 - 0.3" | bc | sed -r 's/^(-?)\./\10./'
For positive numbers, it may be as simple as printing (an string) zero:
$ echo '"0";0.1+0.1' | bc
0.2
avoid the zero if the number is bigger (or equal) to 1:
$ echo 'x=0.1+0.1; if(x<1){"0"}; x' | bc
0.2
It gets a bit more complex if the number may be negative:
echo 'x= 0.3 - 0.5 ; s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1) {"0"};x' | bc
-0.2
You may define a function and add it to a library:
$ echo 'define leadzero(x){auto s;
s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1){"0"};
return(x)};
leadzero(2.1-12.4)' | bc
-10.3
$ echo 'define leadzero(x){auto s;
s=1;if(x<0){s=-1};x*=s;if(s<0){"-"};if(x<1){"0"};
return(x)};
leadzero(0.1-0.4)' | bc
-0.3
Probably, bc isn't really the best "bench calculator" for the modern age. Other languages will give you more control. Here are working examples that print values in the range (-1.0..+1.0) with a leading zero. These examples use bc, AWK, and Python 3, along with Here String syntax.
#!/bin/bash
echo "using bc"
time for (( i=-2; i<=+2; i++ ))
{
echo $(bc<<<"scale=1; x=$i/2; if (x==0||x<=-1||x>=1) { print x } else { if (x<0) { print \"-0\";-x } else { print \"0\";x } } ")
}
echo
echo "using awk"
time for (( i=-2; i<=+2; i++ ))
{
echo $(echo|awk "{printf \"%.1f\",$i/2}")
}
echo
echo "using Python"
time for (( i=-2; i<=+2; i++ ))
{
echo $(python3<<<"print($i/2)")
}
Note that the Python version is about 10x slower, if that matters (still very fast for most purposes).
Doing any non-trivial math with sh or bc is a fool's errand. There are much better bench calculators available nowadays. For example, you can embed and execute Python subroutines inside your Bash scripts using Here Documents.
function mathformatdemo {
python3<<SCRIPT
import sys
from math import *
x=${1} ## capture the parameter from the shell
if -1<=x<=+1:
#print("debug: "+str(x),file=sys.stderr)
y=2*asin(x)
print("2*asin({:2.0f})={:+6.2f}".format(x,y))
else: print("domain err")
SCRIPT
}
echo "using Python via Here-doc"
time for (( i=-2; i<=+2; i++ ))
{
echo $(mathformatdemo $i)
}
Output:
using Python via Here-doc
domain err
2*asin(-1)= -3.14
2*asin( 0)= +0.00
2*asin( 1)= +3.14
domain err
this only uses bc, and works with negative numbers:
bc <<< "x=-.1; if(x==0) print \"0.0\" else if(x>0 && x<1) print 0,x else if(x>-1 && x<0) print \"-0\",-x else print x";
try it with:
for y in "0" "0.1" "-0.1" "1.1" "-1.1"; do
bc <<< "x=$y; if(x==0) print \"0.0\" else if(x>0 && x<1) print 0,x else if(x>-1 && x<0) print \"-0\",-x else print x";
echo;
done
Another simple way, similar to one of the posts in this thread here:
echo 'x=0.1+0.1; print "0",x,"\n"' | bc
Print the list of variables, including the leading 0 and the newline.
Since you have the question tagged [bash] you can simply compute the answer and save it to a variable using command substitution (e.g. r="$(...)") and then using [[..]] with =~ to test if the first character in the result is [1-9] (e.g. [[ $r =~ ^[1-9].*$ ]]), and if the first character isn't, prepend '0' to the beginning of r, e.g.
r=$(echo "0.1 + 0.1" | bc) # compute / save result
[[ $r =~ ^[1-9].*$ ]] || r="0$r" # test 1st char [1-9] or prepend 0
echo "$r" # output result
Result
0.2
If the result r is 1.0 or greater, then no zero is prepended, e.g. (as a 1-liner)
$ r=$(echo "0.8 + 0.6" | bc); [[ $r =~ ^[1-9].*$ ]] || r="0$r"; echo "$r"
1.4
I know it's really stupid question, but I don't know how to do this in bash:
20 / 30 * 100
It should be 66.67 but expr is saying 0, because it doesn't support float.
What command in Linux can replace expr and do this equalation?
bc will do this for you, but the order is important.
> echo "scale = 2; 20 * 100 / 30" | bc
66.66
> echo "scale = 2; 20 / 30 * 100" | bc
66.00
or, for your specific case:
> export ach_gs=2
> export ach_gs_max=3
> x=$(echo "scale = 2; $ach_gs * 100 / $ach_gs_max" | bc)
> echo $x
66.66
Whatever method you choose, this is ripe for inclusion as a function to make your life easier:
#!/bin/bash
function pct () {
echo "scale = $3; $1 * 100 / $2" | bc
}
x=$(pct 2 3 2) ; echo $x # gives 66.66
x=$(pct 1 6 0) ; echo $x # gives 16
just do it in awk
# awk 'BEGIN{print 20 / 30 * 100}'
66.6667
save it to variable
# result=$(awk 'BEGIN{print 20 / 30 * 100}')
# echo $result
66.6667
I generally use perl:
perl -e 'print 10 / 3'
As reported in the bash man page:
The shell allows arithmetic expressions to be evaluated, under certain circumstances...Evaluation is done in fixed-width integers with no check for overflow, though division by 0 is trapped and flagged as an error.
You can multiply by 100 earlier to get a better, partial result:
let j=20*100/30
echo $j
66
Or by a higher multiple of 10, and imagine the decimal place where it belongs:
let j=20*10000/30
echo $j
66666
> echo "20 / 30 * 100" | bc -l
66.66666666666666666600
This is a simplification of the answer by paxdiablo. The -l sets the scale (number of digits after the decimal) to 20. It also loads a math library with trig functions and other things.
Another obvious option:
python -c "print(20 / 30 * 100)"
assuming you are using Python 3. Otherwise, use python3.
In the same thread as this question, I am giving this another shot and ask SO to help address how I should take care of this problem. I'm writing a bash script which needs to perform the following:
I have a circle in x and y with radius r.
I specify resolution which is the distance between points I'm checking.
I need to loop over x and y (from -r to r) and check if the current (x,y) is in the circle, but I loop over discrete i and j instead.
Then i and j need to go from -r/resolution to +r/resolution.
In the loop, what will need to happen is echo "some_text i*resolution j*resolution 15.95 cm" (note lack of $'s because I'm clueless). This output is what I'm really looking for.
My best shot so far:
r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution") | bc
for (( i=-end; i<=end; i++ ));do
for (( j=-end; j<=end; j++ ));do
x=$(echo "scale=5;$i*$resolution") | bc
y=$(echo "scale=5;$j*$resolution") | bc
if (( x*x + y*y <= r*r ));then <-- No, r*r will not work
echo "some_text i*resolution j*resolution 15.95 cm"
fi
done
done
I've had just about enough with bash and may look into ksh like was suggested by someone in my last question, but if anyone knows a proper way to execute this, please let me know! What ever the solution to this, it will set my future temperament towards bash scripting for sure.
You may want to include the pipe into bc in the $()'s. Instead of.
end=$(echo "scale=0;$r/$resolution") | bc
use
end=$(echo "scale=0;$r/$resolution" | bc)
should help a bit.
EDIT And here's a solution.
r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution" | bc)
for i in $(seq -${end} ${end}); do
for j in $(seq -${end} ${end}); do
x=$(echo "scale=5;$i*$resolution" | bc)
y=$(echo "scale=5;$j*$resolution" | bc)
check=$(echo "($x^2+$y^2)<=$r^2" | bc)
if [ ${check} -eq '1' ]; then
iRes=$(echo "$i*$resolution" | bc)
jRes=$(echo "$j*$resolution" | bc)
echo "some_text $iRes $jRes 15.95 cm"
fi
done
done
As already mentioned this problem is probably best solved using bc, awk, ksh or another scripting language.
Pure Bash. Simple problems which actually need floating point arithmetic sometimes can be transposed to some sort of fixed point arithmetic using only integers. The following solution simulates 2 decimal places after the decimal point.
There is no need for pipes and external processes inside the loops if this precision is sufficient.
factor=100 # 2 digits after the decimal point
r=4050 # the representation of 40.50
resolution=250 # the representation of 2.50
end=$(( (r/resolution)*factor )) # correct the result of the division
for (( i=-end; i<=end; i+=factor )); do
for (( j=-end; j<=end; j+=factor )); do
x=$(( (i*resolution)/factor )) # correct the result of the division
y=$(( (j*resolution)/factor )) # correct the result of the division
if [ $(( x*x + y*y )) -le $(( r*r )) ] ;then # no correction needed
echo "$x $y ... "
fi
done
done
echo -e "resolution = $((resolution/factor)).$((resolution%factor))"
echo -e "r = $((r/factor)).$((r%factor))"
you haven't heard of (g)awk ??. then you should go learn about it. It will benefit you for the long run. Translation of your bash script to awk.
awk 'BEGIN{
r=40.5
resol=2.5
end = r/resol
print end
for (i=-end;i<=end;i++) {
for( j=-end;j<=end;j++ ){
x=sprintf("%.5d",i*resol)
y=sprintf("%.5d",j*resol)
if ( x*x + y*y <= r*r ){
print ".......blah blah ......"
}
}
}
}'
It's looking more like a bc script than a Bash one any way, so here goes:
#!/usr/bin/bc -q
/* -q suppresses a welcome banner - GNU extension? */
r = 40.5
resolution = 2.5
scale = 0
end = r / resolution
scale = 5
for ( i = -end; i <= end; i++ ) {
/* moved x outside the j loop since it only changes with i */
x = i * resolution
for ( j = -end; j <= end; j++ ) {
y = j * resolution
if ( x^2 * y^2 <= r^2 ) {
/*
the next few lines output on separate lines, the quote on
a line by itself causes a newline to be created in the output
numeric output includes newlines automatically
you can comment this out and uncomment the print statement
to use it which is a GNU extension
*/
/* */
"some_text
"
i * resolution
j * resolution
"15.95 cm
"
/* */
/* non-POSIX:
print "some_text ", i * resolution, " ", j * resolution, " 15.95 cm\n"
*/
}
}
}
quit