value too great for base (error token is "3ApplicationDesktop") - bash

My simple bash program is giving the error stated in the title.
#!/bin/sh
num1=$1
whatOperator=$2
num2=$3
echo Answer: $(($num1$whatOperator$num2))
I have tried adding 10#
echo Answer: $((10#$num1$whatOperator$num2))
as said to do when I have looked this up, but then it gives this error:
value too great for base (error token is "10#3ApplicationsDesktop")
I was wondering if anybody knows what my problem was and how to fix it!

Try adding a space between:
echo "Answer: $(($num1 $whatOperator $num2))"
Only integers are allowed. And the operators you can use are:
** exponentiation
*, /, % multiplication, division, remainder
+, - addition, subtraction
<<, >> left and right bitwise shifts
<=, >=, <, > comparison
==, != equality, inequality
& bitwise AND
^ bitwise XOR
| bitwise OR
&& logical AND
|| logical OR
See help let.
I managed to solve my problem but, I don't know why it is fixed.
calc 3 * 3
gives an error but if I enter
calc 3*3
it works perfectly.
Because * expands as a glob pattern (try echo *) and 3*3 did not match any file so it stayed the way it is literally that your code worked. You should quote glob patterns:
calc 3 '*' 3
calc 3 "*" 3
calc 3 \* 3
calc 3 $'*' 3

I managed to solve my problem but, I don't know why it is fixed.
calc 3 * 3
gives an error but if I enter
calc 3*3
it works perfectly.
Even though I solved the error I was wondering if anybody knows why it is fixed.

The 10# should be added before $num1 and $num2. 10# is useful in case where one of your integers starts with a 0 (example: 10#012 = 12, instead of 012 = 10).
But I think that is not your problem: Looking at the error message you gave, I am pretty sure variables whatOperator and num2 are not what you expect.
Try adding set -xv at the beginning of the script and set +xv at the end ; or echo "whatOperator=$whatOperator. and num2=$num2." (the dots used to delimit the string, to easily see whether there is a line break). I predict whatOperator=Applications. and num2=Desktop.
I would review the call of the script.

Expanding on #konsolebox's answer, the following bash function might prove useful:
function [[[ () {
lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./}
rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./}
case "$2" in
-lt) return $(( 10#$lhs < 10#$rhs )) ;;
-le) return $(( 10#$lhs <= 10#$rhs )) ;;
-eq) return $(( 10#$lhs == 10#$rhs )) ;;
-ge) return $(( 10#$lhs >= 10#$rhs )) ;;
-gt) return $(( 10#$lhs > 10#$rhs )) ;;
esac
}
Usage might go something like:
n=0.83
if [[[ $n -gt 1 ]]]; then
# do something
fi
Note that the trailing ]]] is ignored.

Related

Addin 0 in front of one digit number/text in bash script

I would like to compare a number, which is the output of a command, with a constant and do some manipulation. That is, if $id < 10, I want to see 590$id and if it is above 10, I want to see 59$id.
I found that expr doesn't working here:
ID=3
NUM=59$ID
if [ `expr $ID` -lt 10]; then
NUM=590$ID
fi
echo $NUM
The output of the code is 593 and not 5903. Even, $(($ID + 5900)) -lt 5910 writes 593.
How can I fix that?
Could you please try following.
cat script.sh
#!/bin/bash
ID=$(printf "%02d" 3 )
##NUM=59$ID ##Commented this to check if, condition is getting satisfied or not. Doesn't seem to be fit here.
(( $ID < 10 )) && NUM="59$ID"
echo "$NUM"
Output will be 5903 after running above code.
Don't use expr. It's old and tricky.
Don't use backticks `. They are discouraged and $( ... ) is preferred.
For arithmetic comparisons use arithmetic expansions. Just
if (( ID < 10 )); then
Note that bash is space aware and your script has a syntax error, it is missing a space - the 10]; should be 10 ];.
Note that by convention uppercase variables should be used for exported variables.
Looking at your code I think you just want:
NUM=$((5900 + ID))

Syntax error in BASH script near `%` operator

I am writing a simple bash script to count the number of occurrences of random draws of cards. I store those in an array, and when printing out the results, for every 10 times that card is pulled, I print one single '*' in a sort of Histogram style of output.
Although, I keep receiving this error when compiling on Terminal:
"task1.sh: line 29: % 10 : syntax error: operand expected (error token is "% 10 ")
task1.sh: line 33: % 10: syntax error: operand expected (error token is "% 10")"
Can't seem to figure out why though. Thank you in advance for any help.
#!/bin/bash
randomdraw(){
Suits="Clubs Diamonds Hearts Spades"
suit=($Suits)
Denominations="2 3 4 5 6 7 8 9 10 Jack Queen King Ace"
denomination=($Denominations)
num_suits=${#suit[*]}
num_denominations=${#denomination[*]}
declare -a numoccurences
declare -a suitoccurences
for ((x=0 ; $x<$loopnum ; x=$x+1));
do
(( numoccurences[$(( RANDOM%num_denominations ))]++ ))
(( suitoccurences[$(( RANDOM%num_suits ))]++ ))
done
}
echo "How Many Random Draws?: "
read loopnum
randomdraw loopnum
for ((x=0 ; $x<$num_denominations ; x=$x+1));
do
let "rounder=$(( ${numoccurences[x]} % 10 ))"
if [ $rounder -ge 5 ];
then
let "starnum=$(( $(( ${numoccurences[x]} / 10 )) + 1 ))"
else
let "starnum=$(( ${numoccurences[x]} / 10 ))"
fi
echo "${denomination[x]}: "
for ((k=0 ; $k<$starnum ; k=$k+1));
do
echo "*"
done
done
Your num_denominations array is mostly empty and the
let "rounder=$(( ${numoccurences[x]} % 10 ))"
is evaluated to
let "rounder=$(( % 10 ))"
Print numoccurences and suitoccurences before asking for loop number for debugging.
You should try to be consistent in the way you write arithmetic expressions in bash. You don't need to use $ to introduce a variable inside an arithmetic expression. And you don't need to use ${array[idx]} either. There's no reason to use let if you have arithmetic evaluation, either. So instead of
let "rounder=$(( ${numoccurences[x]} % 10 ))"
You could write:
(( rounder = numoccurences[x] % 10 ))
These don't quite do the same thing. In the first one, ${numoccurences[x]} will be substituted with nothing if numoccurrences doesn't have a value corresponding to the key $x. In the second one, numoccurrence[x] will be replaced by 0, which is what you actually want. (That has nothing to do with the unnecessary let, since the $((...)) arithmetic expression is evaluated before let is run.)
There are many other places in that script where you would be well advised to simplify your style. For example,
let "starnum=$(( $(( ${numoccurences[x]} / 10 )) + 1 ))"
would be more robust and more readable as
(( starnum = numoccurences[x] / 10 + 1 ))

How to round a large number in Shell command?

In Mac terminal, I would like to round a large number.
For example,
At 10^13th place:
1234567812345678 --> 1230000000000000
Or at 10^12th place:
1234567812345678 --> 1235000000000000
So I would like to specify the place, and then get the rounded number.
How do I do this?
You can use arithmetic expansion:
$ val=1234567812345678
$ echo $(( ${val: -13:1} < 5 ? val - val % 10**13 : val - val % 10**13 + 10**13 ))
1230000000000000
$ echo $(( ${val: -12:1} < 5 ? val - val % 10**12 : val - val % 10**12 + 10**12 ))
1235000000000000
This checks if the most significant removed digit is 5 or greater, and if it is, the last significant unremoved digit is increased by one; then we subtract the division remainder from the (potentially modified) initial value.
If you don't want to have to write it this way, you can wrap it in a little function:
round () {
echo $(( ${1: -$2:1} < 5 ? $1 - $1 % 10**$2 : $1 - $1 % 10**$2 + 10**$2 ))
}
which can then be used like this:
$ round "$val" 13
1230000000000000
$ round "$val" 12
1235000000000000
Notice that quoting $val isn't strictly necessary here, it's just a good habit.
If the one-liner is too cryptic, this is a more readable version of the same:
round () {
local rounded=$(( $1 - $1 % 10**$2 )) # Truncate
# Check if most significant removed digit is >= 5
if (( ${1: -$2:1} >= 5 )); then
(( rounded += 10**$2 ))
fi
echo $rounded
}
Apart from arithmetic expansion, this also uses parameter expansion to get a substring: ${1: -$2:1} stands for "take $1, count $2 from the back, take one character". There has to be a space before -$2 (or is has to be in parentheses) because otherwise it would be interpreted as a different expansion, checking if $1 is unset or null, which we don't want.
awk's [s]printf function can do rounding for you, within the limits of double-precision floating-point arithmetic:
$ for p in 13 12; do
awk -v p="$p" '{ n = sprintf("%.0f", $0 / 10^p); print n * 10^p }' <<<1234567812345678
done
1230000000000000
1235000000000000
For a pure bash implementation, see Benjamin W.'s helpful answer.
Actually, if you want to round to n significant digits you might be best served by mixing up traditional math and strings.
Serious debugging is left to the student, but this is what I quickly came up with for bash shell and hope MAC is close enough:
function rounder
{
local value=$1;
local digits=${2:-3};
local zeros="$( eval "printf '0%.0s' {1..$digits}" )"; #proper zeros
# a bit of shell magic that repats the '0' $digits times.
if (( value > 1$zeros )); then
# large enough to require rounding
local length=${#value};
local digits_1=$(( $digits + 1 )); #digits + 1
local tval="${value:0:$digits_1}"; #leading digits, plus one
tval=$(( $tval + 5 )); #half-add
local tlength=${#tval}; #check if carried a digit
local zerox="";
if (( tlength > length )); then
zerox="0";
fi
value="${tval:0:$digits}${zeros:0:$((length-$digits))}$zerox";
fi
echo "$value";
}
See how this can be done much shorter, but that's another exercise for the student.
Avoiding floating point math due to the inherit problems within.
All sorts of special cases, like negative numbers, are not covered.

Solving a (simple) numeric exercise in bash

Some of you are probably familiar with Project Euler, and I'm currently attempting a few of their problems to teach myself some more bash. They're a bit more mathematical than 'script-y' but it helps with syntax etc.
The problem currently asks me to solve:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
The code I have looks like so:
#!/bin/bash
i="1"
for i in `seq 1 333`
do
threes[$i]=`calc $i*3` # where 'calc' is a function written in bashrc
#calc actually looks like: calc() {awk "BEGIN { print "$*"} }
let "sumthrees = sumthrees + ${threes[$i]}"
done
for i in `seq 1 199`
do
fives[$i]=`calc $i*5`
let "sumfives = sumfives + ${fives[$i]}"
done
let "ans = $sumfives + $sumthrees"
echo "The sum of all 3 factors is $sumthrees and the sum of all five factors is $sumfives"
echo "The sum of both is $ans"
#So I can repeatedly run the script without bash remembering the variables between executions
unset i
unset fives
unset threes
unset sumfives
unset sumthrees
unset ans
So far I've not gotten the correct answer, but have run out of ideas as to where I'm going wrong. (FYI, the script currently gives me 266333, which I believe is close, but I don't know the answer yet.)
Can anyone spot anything? And for my own learning, if there are more elegant solutions to this that people might like to share that would be great.
EDIT
Thanks for all the answers, super informative. Since there are so many useful answers here I'll accept my favourite as the proper thread answer.
Blue Moon pointed out the actual problem with your logic.
You don't need to store all the threes and fives in arrays because you don't need them later.
You don't need to unset variables at the end of a script if you use ./yourscript or bash script because they'll disappear along with the
shell instance (better to initialize them first in any case).
You don't need awk to do math, bash does that just fine.
seq and let are not the best way to do anything in a bash script.
Here's a straight forward version:
#!/bin/bash
sum=0
for ((i=1; i<1000; i++))
do
if (( i%3 == 0 || i%5 == 0 ))
then
(( sum += i ))
fi
done
echo "$sum"
Your logic is almost right except that there are numbers which divide by both 3 and 5. So you are adding these numbers twice. Hence, you get wrong answer.
Use another loop similar to ones you have and subtract the ones that divide by both 3 and 5 from the result.
A few tips you might find useful:
In bash, you use let to give the shell a hint that a variable should be considered a number. All bash variables are strings, but you can do arithmetic on numerical strings. If I say let i=1 then i is set to 1, but if I say let i="taco" then $i will be 0, because it couldn't be read as a number. You can achieve a small amount of type-safety when doing mathematical work in the shell.
Bash also has $((this)) mechanism for doing math! You can check it out yourself: echo $((2 + 2)) -> 4, and even more relevant to this problem: echo $((6 % 3 == 0)) -> 1
In case you aren't familiar, % divides the first number by the second, and gives back the remainder; when the remainder is 0, it means that the first is divisible by the second! == is a test to see if two things are equal, and for logical tests like this 1 represents true and 0 represents false. So I'm testing if 6 is divisible by 3, which it is, and the value I get back is 1.
The test brackets, [ ... ] have a "test for equality" flag, -eq, which you can use to check if a math expression has a certain value (man test for more details)!
$ let i=6
$ echo $((i % 3 == 0 || i % 5 == 0))
1
$ if [ $((i % 3 == 0 || i % 5 == 0)) -eq 1 ]; then echo "yes"; fi
yes
(|| is another logical test - $((a || b)) will be 1 (true) when a is true or b is true).
Finally, instead of doing this for the number 6, you could do it in a for loop and increment a sum variable every time you find a multiple of 3 or 5:
let sum=0
for i in {1..1000}; do
if [ $((i % 3 == 0 || i % 5 == 0)) -eq 1 ]; then
let sum=$((sum + i))
fi
done
echo $sum
And there you'd have a working solution!
Bash has a lot of nice little tricks, (and a lot more mean ugly tricks), but it's worth learning at least a handful of them to make use of it as a scripting tool.
How about creative use of the modulus function & some checks. Then you have just 1 loop.
#!/bin/bash
i=1
while [ $i -lt 1000 ]
do
if [ $(($i % 3)) -eq 0 ] || [ $(($i % 5)) -eq 0 ]
then
sumall=$(($sumall+$i))
fi
i=$(($i+1))
done
echo "The sum of both is $sumall"
Answer: 233168
A different solution:
#!/bin/bash
sum=0
for n in {1..999}; do [ $(((n%5) * (n%3))) -eq 0 ] && sum=$((sum+n)); done
echo $sum
The script loops through all numbers below 1000, tests if the product of the number mod 3 and the number mod 5 is 0 (the product of two numbers can only be zero if one of them is zero). If that is the case, it adds the current number to a sum, which is printed out afterwards.
By the way, if I were you I'd include the definition of the calc function inside the script, to get a self-contained solution that doesn't need your specific configuration.

shell script + numbers sum

what the best simple elegant way to sum number in ksh or bash
my example is about let command , but I want to find better way to summary all numbers
for example
num1=1232
num2=24
num3=444
.
.
.
let SUM=$num1+num2+num3.........
How about:
num1=1232
num2=24
num3=444
sum=$((num1+num2+num3))
echo $sum # prints 1700
Agree with ghostdog74. I once used $(( )) built-in function, but I changed to bc because the format the we receive data is not very "number-formated". Check below:
jyzuz#dev:/tmp> echo $(( 017 + 2 ))
17
jyzuz#dev:/tmp> echo $(( 17 + 2 ))
19
jyzuz#dev:/tmp>
Seems that in the 1st case it understands as binary or hex numbers.. not very sure.
So I changed to bc. You can choose wich way you prefer:
bc << EOF
$num1 + $num2 + $num3
EOF
or
bc <<< "$num1 + $num2 + $num3"
There are other cools ways to do this...but it would be good if you send more details, like if you're performing division also, you'll need to add bc -l argument, to load math lib.
You can eliminate the last dollar sign and freely space the operands and operators (including the variable and the assignment operator) for readability if you move the double parentheses all the way to the outside.
num1=1232
num2=24
num3=444
(( sum = num1 + num2 + num3 ))
(( count++ ))
(( sum += quantity ))
You can't use the increment style operators (*= /= %= += -= <<= >>= &= ^= |= ++ --) unless you use let or the outside (()) form (or you're incrementing variables or making assignments on the right hand side).
you can use $(()) syntax, but if you have decimal numbers, use awk, or bc/dc to do your maths, "portably".

Resources