Bash round whole numbers up, down or to nearest hundreds - bash

There is a range of whole positive numbers like
827818
6574762
685038
55326902
What I need, for example, to round down to hundreds to get accordingly
827800
6574700
685000
55326900
Many ideas how to round up, down or nearest hundreds using Javascript, for example
Math.floor(number / 100) * 100;
but is it possible to do the same in Bash ?

It's not entirely clear what is meant by "in Bash", but perhaps one of :
$ cat input
827818
6574762
685038
55326902
$ awk '{printf "%d00\n", $0 / 100}' input
827800
6574700
685000
55326900
or (if all the values are greater than 100!):
while read x; do echo "${x%[0-9][0-9]}00"; done < input
If you want to handle the case where values are less than 100 and deal with negative values, you could do:
while read x; do if test "${x#-}" -gt 100; then echo "${x%[0-9][0-9]}00"; else echo 0; fi; done < input
but it's almost certainly better to stick with awk.

To round to the nearest 100, use (number + 50) / 100 * 100.
You can modify each line to this calculation, then run those expressions through bc to evaluate them.
sed 's|.*|(&+50)/100*100|' file | bc

For rounding down you can also rev the full number and use cut -c 1-2 on the output, which you can then rev again to obtain the full number's last two digits. Then you can do calc or bc (full number - first output) to round down to nearest 100.
Or you can remove the last two digits of the full number with bc using scale=0 (or no scale) and divide by 100, after which you can just multiply by 100 again;
DIV_100=$( echo "123456789/100" | bc ) && echo "$DIV_100*100" | bc
and in loop
while read -r line; do
echo "$line" > full_number_1.txt
FULL_NUMBER=$(<full_number_1.txt)
DIV_100=$( echo "$FULL_NUMBER / 100" | bc )
echo "$DIV_100 * 100" | bc >> output_file
done < input.txt
Or use echo ${...%??}
echo "${number%??}*100" | bc
..loop..
while read -r line; do
echo "${line%??}*100" | bc >> output
done < input.txt
Many options here for rounding down.
Rounding up will require some alterations but is equally possible in many ways. For example:
DIV_100=$( echo "123456789/100" | bc ) && echo "($DIV_100*100)+100" | bc
and in loop
while read -r line; do
echo "$line" > full_number_1.txt
FULL_NUMBER=$(<full_number_1.txt)
DIV_100=$( echo "$FULL_NUMBER / 100" | bc )
echo "($DIV_100 * 100)+100" | bc >> output_file
done < input.txt
or with echo ${...%??}
echo "(${number%??}*100)+100" | bc
..loop..
while read -r line; do
echo "(${line%??}*100)+100" | bc >> output
done < input.txt

Related

Product of a digit with a shell script

How do I get the product of a 1 number in bash? (12345 > 1x2x3x4x5)
I am trying to get a script to do multiplication, I have tried escaping the * char but no luck, I have also tried fiddling with expr.
echo -n "Enter number "
read n
len=$(echo $n | wc -c)
len=$(( $len - 1 ))
for (( i=1; i <= $len; i++ ))
do
prod=$(($prod \* $(echo $n | cut -c $i) ))
done
echo "Product of $n is $prod"
You can get the length of a string from parameter expansion, no need to call external tools.
#!/bin/bash
read -p 'Enter number: ' n
product=1
for (( i=0; i < ${#n}; ++i )) ; do
d=${n:i:1}
(( product *= d ))
done
printf '%d\n' $product
And, for fun, using Perl:
perl -MList::Util=product -le 'print product(split //, shift)' -- "$n"
gawk (GNU awk) solution:
echo 12345 | awk -v FPAT="[0-9]" '{ r=$1; for(i=2;i<=NF;i++) r=r*$i }END{ print r }'
The output:
120
FPAT="[0-9]" - represents field value so that each field contains a digit
As I understood you want to get value of digits multiplication in number
Suppose you have number:
number=12345
You have to insert * between digits:
echo $number | sed 's/\([0-9]\)/\1*/g'
We will get string:
1*2*3*4*5*
We don't need last asteriks - let's remove it:
echo $number | sed 's/\([0-9]\)/\1*/g' | sed 's/.$//g'
We will get this:
1*2*3*4*5
We can now redirect it to calc:
echo $number | sed 's/\([0-9]\)/\1*/g' | sed 's/.$//g' | calc -p
It's stdout is:
120
\* is wrong in an arithmetic expression, it should be * alone. But even then running your code gives:
$ bash product.sh
Enter number 12
product.sh: line 10: * 1 : syntax error: operand expected (error token is "* 1 ")
Product of 12 is
The reason for the error is that $prod variable is not set to an
initial value before so it's expanded to an empty value, for example
try it in your terminal:
$ echo $prod
$
In your script you should set prod to an initial value before using
it for the first time. It should be:
echo -n "Enter number "
read n
len=$(echo $n | wc -c)
len=$(( $len - 1 ))
prod=1
for (( i=1; i <= $len; i++ ))
do
prod=$(($prod * $(echo $n | cut -c $i) ))
done
echo "Product of $n is $prod"
There are a few more problems with your code:
always put a shebang line at the top
always double quote the variables
using $ on variables is not necessary in arithmetic expressions in Bash

How to calculate extra large numbers with bc in bash

I have a file with 1800 lines that look like this
600.76
600.66
700.44
566.66
Ect..
I made a bash script to calculate the mean.
Now I first made a variable to count the total column lines like:
Lines="$(awk 'END{print NR}' file.txt)"
Then another variable for the sum of that column like this:
Sum="$(awk '{s+1=$1}END {print s}' file.txt)"
Lastly I'm finding the mean like this:
Echo "scale=2 ; $Sum / $Lines" | bc
With debugging enabled It returns:
+echo 'scale=2 ; 1.72161e+06 / 1800'
(Standard_1): syntax error
I realize now bc doesn't do scientific notation but how do I get around this.
I'm OK with short handing the decimal by restricting it to 2 or 3 places.
Unnecessary to use awk. Simple oneliner can do the job.
echo "scale=2; ("$(paste -sd+ file.txt)")"/$(wc -l <file.txt)|bc
Use bc -l for both summation and final division:
sum=0
count=0
while read number; do
number=$(printf "%f\n" $number) # get rid of scientific notation
sum=$(echo "$sum" '+' "$number" | bc -l)
count=$((count + 1))
done < input
avg=$(echo $sum / $count | bc -l)
echo $avg

Rounding up to 3 decimal points just truncates the rest

read n
i=0
sum=0
while [ $i -lt $n ]
do
read X
sum=`expr $X + $sum `
i=`expr $i + 1 `
done
echo "scale = 3; $sum/$n" | bc -l
--my above code is rounding upto a lesser value, where i want the greater one
e.g. if the ans is 4696.9466 it is rounding up to 4696.946 whereas 4696.947 is what i want. So , suggest any edits
You may pipe your bc to printf :
echo "scale = 4; $sum/$n" | bc -l | xargs printf '%.*f\n' 3
From you example :
$ echo "scale = 4; 4696.9466" | bc -l | xargs printf '%.*f\n' 3
4696,947
Change last line of your script from echo "scale = 3; $sum/$n" | bc -l to
printf %.3f $(echo "$sum/$n" | bc -l)
printf will round it off correctly. For example,
$ sum=1345
$ n=7
$ echo "$sum/$n" | bc -l
192.14285714285714285714
$ printf %.3f $(echo "$sum/$n" | bc -l)
192.143

Bash - invalid arithmetic operator

I'm trying to study for a test and one of the subjects are bash scripts.
I have the following txt file :
123456 100
654321 50
203374111 86
I need to get the averages of the scores (the numbers in the second column).
This is what I have written :
cat $course_name$end | while read line; do
sum=`echo $line | cut -f2 -d" "`
let total+=$sum
done
I have tried with
while read -a line
and then
let sum+=${line[1]}
But I'm still getting the same error mentioned in the header.
I love AWK:
awk -F\* '{sum+=$3} END {print sum/NR}' x.txt
So in x.txt are values are stored. Please note that many answers don't actually compute the average, as they need to divide by the line numbers in the end. Often it will be performed by a wc -l < x.txt but in my solution you will get it almost for free.
cat your_file_name.txt | cut -f2 -d" " | paste -sd+ | bc
This should do the job!
You are very close, this works for me:
while read line; do
sum=$(echo $line | cut -f2 -d" ")
echo "sum is $sum"
let total+=$sum
echo "total is $total"
done < file
echo "total is $total"
As you can see, there is no need to use cat $course_name$end, it is enough to do
while read line
do
done < file
Also, it is more recommendable to use
sum=$(echo $line | cut -f2 -d" ")
rather than
sum=`echo $line | cut -f2 -d" "`
Or even
sum=$(cut -f2 -d" " <<< "$line")
There's no need to use cat as well as read; you can redirect the contents of the file into the loop. You also don't need to use let for arithmetic.
sum = 0
count = 0
while read id score; do
(( sum += score )) && (( ++count ))
done < "$course_name$end"
echo $(( sum / count ))
This will give you an integer result, as bash doesn't do floating point arithmetic. To get a floating point result, you could use bc:
bc <<< "scale=2;$a/$b"
This will give you a result correct to 2 decimal places.

Assigning variables to values in a text file with 3 columns, line by line

I've got a .txt file with three columns, each separated by a tab, and 264 rows called PowerCoords.txt. Each row contains an x (column 1), y (column2) and z (column3) coordinate. I want to go through this file, line by line, assign each value to X,Y, and Z, and then input those variables into another function.
I'm new to bash, and I don't understand how to specify that I want the value in Row 1, Column 2 to be the variable Y, and so on...
I know this is likely super simple and I could do it in a flash in Matlab, but I'm trying to keep everything in bash.
while read x y z; do
echo x=$x y=$y z=$z
done < input.txt
The above requires that none of your columns contain any whitespace.
EDIT:
In response to comments, here is one technique to handle numbering the lines:
nl -ba < input.txt | while read line x y z rest; do
~/data/standard/MNI152_T1_2mm -mul 0 \
-add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float > NewFile$line
done
William Pursell's answer is much smarter, but in my straight-forward beginners mind I tried following some time ago:
#!/bin/bash
data="data.dat"
datalength=`wc $data | awk '{print $1;}'`
for (( i=1; i<=$datalength; i++ )) ;do
x=`cat $data | awk '{print $1;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; x=`echo "$x" | bc -l` ; echo "x$i=$x";
y=`cat $data | awk '{print $2;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; y=`echo "$y" | bc -l` ; echo "y$i=$y";
z=`cat $data | awk '{print $3;}' | sed -n "$i"p | sed -e 's/[eE]+*/\\*10\\^/'` ; z=`echo "$z" | bc -l` ; echo "z$i=$z";
# do something with xyz:
fslmaths ~/data/standard/MNI152_T1_2mm -mul 0 -add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float > NewFile$i
done
The bc and the sed -e 's/[eE]+*/\\*10\\^/' have to be added if you like to use floating point numbers and for the case that input also uses exponential notation.
I had a similar problem but for lots of input data those bash scripts are very slow. I migrated to perl then. In perl it would look like this:
#!/usr/bin/perl -w
use strict;
open (IN, "data.dat") or die "Error opening";
my $i=0;
for my $line (<IN>){
$i++;
open(OUT, ">NewFile$i.out");
chomp $line;
(my $x,my $y,my $z) = split '\t',$line;
print "$x $y $z\n";
# do something with xyz:
my $f= fslmaths ~/data/standard/MNI152_T1_2mm -mul 0 -add 1 -roi $x 1 $y 1 $z 1 0 1 point -odt float
print OUT "f= $f\n";
close OUT;
}
close IN;

Resources