Bash modulo strange result (not negative) - bash

I want to use modulo in a Bash script, but the result is always to low.
I need something like this: days = jdn mod p
this is what i have:
#!/bin/bash
if [ $# -ne 2 ]
then
echo "Fout, syntaxis: $0 maand(in cijfers) jaar"
exit 1
fi
a=$(echo "14 - $1"|bc)
y=$(echo "$2 + 4800 - $a"|bc)
m=$(echo "12 * $a - 3 + $1"|bc)
jdn=$(echo "scale=2;1 + (153 * $m +2)/5 + 365 * $y +${y}/4 - ${y}/100 - 32045"|bc)
jdn=$(echo "$jdn + 1"|bc|sed "s/...$//")
d=1
p=$(echo "29.530588853")
while [ "$d" != "32" ]
do
echo -n "$d"
days=$(echo "scale=2;${jdn} % ${p}"|bc)
fase=$(echo "scale=2;$p / $days"|bc)
fase1=$(echo "${fase}<7.382647213"|bc)
if [ $fase1 -eq 1 ]
then
echo -n "#"
elif [ $(echo "${fase}<14.76529443"|bc) -eq 1 ]
then
echo -n ")"
elif [ $(echo "$fase<22.14794164"|bc) -eq 1 ]
then
echo -n "0"
else
echo -n "("
fi
d=$(echo "$d + 1"|bc)
jdn=$(echo "$jdn +1"|bc)
done
jdn has as result 2455369, days should be 9.55 but the result is 0.054...

Arithmetic in Bash shell is done with integers only, so it is better to change the approach (or multiply the qantities by a large power of ten to make them be integers).
When you are doing math with integers in Bash, include the arithmetic in double parentheses, or to use a 'let' keyword.
((${jdn} % ${p}))
as described in these sources.
http://ubuntuforums.org/showthread.php?t=1970559
How to use mod operator in bash?

I've found a solution: without scale, it works fine. So this is my code:
days=$(echo "${jdn} % ${p}"|bc)
I only had to make sure that the modulo did'nt give a 0 back and than i could set scale in the division. It seemed to be more a math question at the end...

Related

Bash syntax errors like `syntax error: operand expected (error token is "{1 % 2 ")`

Please tell why printing odd numbers in bash script with the following code gives the error:
line 3: {1 % 2 : syntax error: operand expected (error token is "{1 % 2 ")
for i in {1 to 99}
do
rem=$(( $i % 2 ))
if [$rem -neq 0];
then
echo $i
fi
done
This is working example:
for i in {1..99}
do
rem=$(($i % 2))
if [ "$rem" -ne "0" ]; then
echo $i
fi
done
used for loop have a typo in minimum and maximum number, should be {1..99} instead of {1 to 99}
brackets of the if statement needs to be separated with whitespace character on the left and on the right side
Comparision is done with ne instead of neq, see this reference.
As already pointed out, you can use this shell checker if you need some clarification of the error you get.
Not really sure why nobody included it, but this works for me and is simpler than the other 'for' solutions:
for (( i = 1; i < 100; i=i+2 )); do echo $i ; done
To print odd numbers between 1 to 99
seq 1 99 | sed -n 'p;n'
With GNU seq, credit to gniourf-gniourf
seq 1 2 99
Example
$ seq 1 10 | sed -n 'p;n'
1
3
5
7
9
if you reverse it will print even
$ seq 1 10 | sed -n 'n;p'
2
4
6
8
10
One liner:
for odd in {1..99..2}; do echo "${odd}"; done
Or print in a cluster.
for odd in {1..99..2}; do echo -n " ${odd} "; done
Likewise, to print even numbers only:
for even in {2..100..2}; do echo "${even}"; done
OR
for even in {2..100..2}; do echo -n " ${even} "; done
Replace {1 to 99} by {1..99}.
for (( i=1; i<=100; i++ ))
do
((b = $i % 2))
if [ $b -ne 0 ]
then
echo $i
fi
done
for i in {1..99}
do
rem=`expr $i % 2`
if [ $rem == 1 ]
then
echo "$i"
fi
done
for i in {0..49}
do
echo $(($i*2+1))
done

Is there a way to calculate factorials of #s w/o a pre-determined amount of user input? i.e. will calculate 1!-7! w/o initializing 7 diff. variables

Is there a way to get rid of having to create 4 different do-while statements for each variable? For example, I would like to calculate 1!-7! w/o initializing 7 diff. variables and initializing 7 different variables. Is this possible?
Below is an example of how I would like the program to initiate and what the output should be
./filename.sh 1 2 3 4 5 6 7
The factorial 1! is 1
The factorial 2! is 2
The factorial 3! is 6
The factorial 4! is 24
…
Code:
#!/bin/bash
on1=0 #initiating the 4 variables
on2=0 #possible to compress these into 1 variable?
on3=0
on4=0
fact1=1
fact2=1
fact3=1
fact4=1
echo -n "Enter numbers to find factorial : " # gets user input of 4 #s
read n1 n2 n3 n4
on1=$n1
on2=$n2
on3=$n3
on4=$n4
while [ $n1 -ge 1 ] # calculates first factorial
do #possible to only make 1 loop?
fact1=`expr $fact1 \* $n1`
n1=`expr $n1 - 1`
done
while [ $n2 -ge 1 ] # calculates second factorial
do
fact2=`expr $fact2 \* $n2`
n2=`expr $n2 - 1`
done
while [ $n3 -ge 1 ] #calculates third factorial
do
fact3=`expr $fact3 \* $n3`
n3=`expr $n3 - 1`
done
while [ $n4 -ge 1 ] # calculates fourth factorial
do
fact4=`expr $fact4 \* $n4`
n4=`expr $n4 - 1`
done
echo "The factorial $on1! is $fact1" # outputs the four factorials
echo "The factorial $on2! is $fact2" #possible to make only 1 echo?
echo "The factorial $on3! is $fact3"
echo "The factorial $on4! is $fact4"
Use a loop to process each of the arguments.
#!/bin/bash
while [ $# -gt 0 ]
do
n=$1
on=$n
fact=1
while [ $n -ge 1 ]
do
fact=$(expr $fact \* $n)
n=$(expr $n - 1)
done
echo "The factorial of $on is $fact"
shift # go to the next argument
done
Just for fun, I tried a recursive solution:
#!/bin/zsh
function fact {
if (($1 < 2))
then
echo 1
else
expr $1 '*' $(fact $(($1-1)))
fi
}
for arg
do
fact $arg
done
I first thought, it would blow up soon due to stack overflow, but it calculated happily even pretty large values (such as 200!) in quite short time (in zsh; didn't try it with other shells)!

Shell script I am trying to get the product of odd numbers

mul=1
i=0
while [ $i -ne 10 ]
do
echo "Enter Number"
read num
if [ `expr $num % 2` -ne 0 ]
then
mul=`expr $mul*$num`
fi
i=`expr $i + 1`
done
echo mul of odd numbers = $mul
this is what i tried...its showing output as 1*3*5*7*9
pls correct the error here
Thanks in advance
"*" has a special meaning, hence you need to escape it and need to have a space between the two variables like:
mul=`expr $mul \* $num`
Note aside- Use of back ticks are discouraged and you may want to use something instead like:
mul=$(expr $mul \* $num)
Since your don't provide some details (see my comment above) I can't guarantee this answers your question and produces the desired result. This assumes your shell is bash. Please inform me and I'll edit the answer accordingly.
Consider the changes below. The relevant part is the change from expr ... to $(( ... )), which is bash's built-in arithmetic expression evaluator.
#!env bash
MUL=1
I=0
while [ $I -ne 10 ]
do
echo "Enter Number"
read NUM
if [[ $(($NUM % 2)) -ne 0 ]] ; then
MUL=$(($MUL * $NUM))
fi
I=$(($I + 1))
done
echo MUL of odd numbers = $MUL
This produces the following output:
$ sh foo.sh
Enter Number
1
Enter Number
2
Enter Number
3
Enter Number
4
Enter Number
5
Enter Number
6
Enter Number
7
Enter Number
8
Enter Number
9
Enter Number
0
MUL of odd numbers = 945

What is the common name of this "binary" approach to counting? How should it be done?

The following script is a study from my work with a script that I use in order to scrape web pages with links to an unknown number of sequentially numbered files. In order to create as few useless requests as possible, the scraping script guesses the number of files in a "binary" way.
This "study" script optionally takes a guess as $1 as a point of departure in order to find the number of .txt files in a directory. As I was writing it, i had the feeling of reinventing the wheel.
What is this way of counting called? How could it be done less clumsily in Bash?
#!/bin/bash
set_SEARCH()
{
DIVIDED=$SEARCH
SEARCH=$(($SEARCH * 2))
}
increase_SEARCH()
{
SEARCH=$(($SEARCH + $DIVIDED))
DIVIDED=$(($DIVIDED / 2))
#echo $DIVIDED
}
decrease_SEARCH()
{
SEARCH=$(($SEARCH - $DIVIDED))
DIVIDED=$(($DIVIDED / 2))
#echo $DIVIDED
}
test_SEARCH()
{
while [ -f "$(($SEARCH * 2)).txt" ]
do
set_SEARCH
done
if [ -f "${SEARCH}.txt" ]
then
echo "Trying $SEARCH"
if [ "$DIVIDED" = 0 ]
then
NUMBER=$(($SEARCH + 0))
echo "The number is $NUMBER"
exit
fi
increase_SEARCH
test_SEARCH
else
echo "Trying $SEARCH"
if [ "$DIVIDED" = 0 ]
then
NUMBER=$(($SEARCH - 1))
echo "The number is $NUMBER"
exit
fi
decrease_SEARCH
test_SEARCH
fi
}
SEARCH=2
while [[ "$SEARCH" -le "$1" ]] ;
do
SEARCH=$(($SEARCH * 2))
done
DIVIDED=$(($SEARCH / 2))
test_SEARCH
This is binary search with unknown upper bound. Knowing this, we can easily write/adapt implementations:
upper=42 # an arbitrary number >= 1
lower=0
while [[ -f $upper.txt ]]
do
lower=$upper
(( upper*= 2 ))
done
while (( lower < upper-1 ))
do
middle=$(((lower+upper)/2))
if [[ -f $middle.txt ]]
then
lower=$middle
else
upper=$middle
fi
done
echo "The number is $lower"

Division in script and floating-point

I would like to do the following operation in my script:
1 - ((m - 20) / 34)
I would like to assign the result of this operation to another variable. I want my script use floating point math. For example, for m = 34:
results = 1 - ((34 - 20) / 34) == 0.588
You could use the bc calculator. It will do arbitrary precision math using decimals (not binary floating point) if you set increease scale from its default of 0:
$ m=34
$ bc <<< "scale = 10; 1 - (($m - 20) / 34)"
.5882352942
The -l option will load the standard math library and default the scale to 20:
$ bc -l <<< "1 - (($m - 20) / 34)"
.58823529411764705883
You can then use printf to format the output, if you so choose:
printf "%.3f\n" "$(bc -l ...)"
Bash does not do floating point math. You can use awk or bc to handle this. Here is an awk example:
$ m=34; awk -v m=$m 'BEGIN { print 1 - ((m - 20) / 34) }'
0.588235
To assign the output to a variable:
var=$(awk -v m=$m 'BEGIN { print 1 - ((m - 20) / 34) }')
Teach bash e.g. integer division with floating point results:
#!/bin/bash
div () # Arguments: dividend and divisor
{
if [ $2 -eq 0 ]; then echo division by 0; return 1; fi
local p=12 # precision
local c=${c:-0} # precision counter
local d=. # decimal separator
local r=$(($1/$2)); echo -n $r # result of division
local m=$(($r*$2))
[ $c -eq 0 ] && [ $m -ne $1 ] && echo -n $d
[ $1 -eq $m ] || [ $c -eq $p ] && echo && return
local e=$(($1-$m))
c=$(($c+1))
div $(($e*10)) $2
}
result=$(div 1080 633) # write to variable
echo $result
result=$(div 7 34)
echo $result
result=$(div 8 32)
echo $result
result=$(div 246891510 2)
echo $result
result=$(div 5000000 177)
echo $result
Output:
1.706161137440
0.205882352941
0.25
123445755
28248.587570621468
echo $a/$b|bc -l
gives the result.
Example:
read a b
echo $a/$b|bc -l
Enter a & b value as 10 3, you get 3.3333333333
If you want to store the value in another variable then use the code
read a b
c=`echo $a/$b|bc -l`
echo $c
It also gives the same result as above.
Try it...
I know this is an old thread, but this seemed like a fun project to tackle without using bc or invoking recursion. I'm sure it can be improved, but this maxed out my skill.
numerator=5
denominator=7 # - 0 -> returns "undef"
decimal_places=4 # - 0 -> same as echo $(( $numerator / $denominator ))
_result_sign=""
let _dp_exp=10**decimal_places
if [ $denominator -eq 0 ]; then _div_result_int_large=0; else let _div_result_int_large=$((numerator * _dp_exp / denominator)); fi
if [ $_div_result_int_large -lt 0 ]; then let _div_result_int_large=$(( _div_result_int_large * -1 )); _result_sign="-"; fi
let _div_result_int=$((_div_result_int_large / _dp_exp))
let _div_result_mant=$((_div_result_int_large - _div_result_int * _dp_exp))
let _dp_lzeros=$((decimal_places - ${#_div_result_mant}))
printf -v _div_result_mant_padded "%.${_dp_lzeros}d$_div_result_mant"
div_result="$_result_sign$_div_result_int"
if [ $decimal_places -gt 0 ]; then div_result="$_result_sign$_div_result_int.$_div_result_mant_padded"; fi
if [ $denominator -eq 0 ]; then div_result="undef"; fi
echo $div_result
Example output:
numerator=5
denominator=7
decimal_places=5
-> 0.71428
numerator=250
denominator=13
decimal_places=0
-> 19
numerator=-5
denominator=6
decimal_places=2
-> -0.83
numerator=3
denominator=0 # - uh-oh
decimal_places=2 # - can be anything, in this case
-> undef
Use this script open this file with favorite editor like:
$ sudo vim /usr/bin/div
Then paste this code:
#!/bin/bash
# Author: Danial Rikhteh Garan (danial.rikhtehgaran#gmail.com)
if [[ -z "$1" ]] || [[ -z "$2" ]]; then
echo "Please input two number"
echo "for 100/50 use: div 10 50"
exit 1;
fi
div=$(echo "$1/$2" | bc -l);
echo 0$div | sed 's/[0]*$//g'
Now chmod it to 755:
$ sudo chmod 755 /usr/bin/div
Now use it:
$ div 5 100
0.05
In your script you can use this:
var=$(div 5 100);
echo "$var"

Resources