what's wrong with my code? I am trying to print prime numbers upto n digits
echo Enter Number
read num
for (( i=2; $i <= $num ; i++ ))
do
c=0
for (( j=2; $j <= $i ; j++))
do
mod=$(($i % $j))
if [ "$mod" -eq 0 ]
then
c=`expr $c+1`
fi
done
if [ "$c" -eq 1 ]
then
echo $c
fi
done
I don't have any idea what I'm doing wrong. If someone could tell me how to fix it I would be thankful
newprime.sh: line 14: [: 0+1: integer expression expected
newprime.sh: line 14: [: 0+1: integer expression expected
newprime.sh: line 14: [: 0+1+1: integer expression expected
newprime.sh: line 14: [: 0+1: integer expression expected
newprime.sh: line 14: [: 0+1+1+1: integer expression expected
newprime.sh: line 14: [: 0+1: integer expression expected
expr requires parameters to be passed as separate arguments. Quoting the POSIX standard for expr:
The application shall ensure that each of the expression operator symbols [...] and the symbols integer and string in the table are provided as separate arguments to expr.
The code here is appending all the operators into a single argument, hence your problem.
Thus:
c=$(expr "$c" + 1)
...NOT...
c=$(expr $c+1)
But don't do that at all. It's more efficient and more readable to write:
c=$(( c + 1 ))
Optimized with less iterations POSIX shell version:
#!/usr/bin/env sh
printf %s 'Enter Number: '
read -r num
i=1
while [ "$i" -le "$num" ]; do
c=0
j=2
# Stop checking division when divisor power 2 is greater than number
# or we identifed a divisor
while [ "$((j * j))" -le "$i" ] && [ "$c" -eq 0 ]; do
c=$((i % j == 0))
j=$((j + 1))
done
if [ "$c" -eq 0 ]; then
printf '%s\n' "$i"
fi
i=$((i + 2))
done
Or using a function:
#!/usr/bin/env sh
is_prime() {
j=2
# Check j is a divisor of argument number, while j^2 is less than number
while [ "$((j * j))" -le "$1" ]; do
# If j is a divisor of number before the end of the loop
# number is not prime, so return 1
[ "$(($1 % j))" -eq 0 ] && return 1
j=$((j + 1))
done
}
printf %s 'Enter Number: '
read -r num
i=1
while [ "$i" -le "$num" ]; do
if is_prime "$i"; then
printf '%s\n' "$i"
fi
i=$((i + 2))
done
Don't use expr. Put your mathematical expression inside (( )) (or echo $(( )) to print the result), and the shell will evaluate it.
See what the expr output looks like, vs regular shell arithmetic:
$ expr 0+1
0+1
$ echo "$((0+1))"
1
-eq with test, or single square brackets (eg [ 1 -eq 2 ]) prints an error if both operands aren't integers. That's what's causing your error.
Here's a fast and concise way to list primes in bash. You can put it in a function or script:
for ((i=2; i<="${1:?Maximum required}"; i++)); do
for ((j=2; j<i; j++)); do
((i%j)) || continue 2
done
echo "$i"
done
edit: Just to explain something, if (([expression])) evaluates to 0, it returns non-zero (1) (failure). If it evaluates to any other number (positive or negative), it returns zero (true). When a number divides evenly in to i, the modulo (%) (remainder) is zero. Hence the command fails, we know it's not prime, and we can continue the outer loop, for the next number.
Related
I have a slight problem with my BASH script that I do not know the cause.
Please take a look at this simple script.
#!/bin/bash
# Given two integers X and Y. find their sum, difference, product, and quotient
# Constraints
# -100 <= X,Y <= 100
# Y != 0
# Output format
# Four lines containing the sum (X+Y), difference (X-Y), product (X x Y), and the quotient (X // Y) respectively.
# (While computing the quotient print only the integer part)
# Read data
echo "Please input for x and y!"
read x
read y
declare -i MIN=-100
declare -i MAX=100
# Checks if the valued read is in the constraints
if [ $x -gt $MAX ] || [ $x -lt $MIN ];
then
echo "Error!"
exit 1;
elif [ $y -gt $MAX ] || [$y -lt $MIN ] || [$y -eq 0];
then
echo "Error"
exit 1;
else
for operator in {"+","-","*","/",}; do echo "$x $operator $y" | bc; done
fi
The output of the script above is as follow.
Please input for x and y!
1
2
worldOfNumbers.sh: line 26: [2: command not found
worldOfNumbers.sh: line 26: [2: command not found
3
-1
2
0
As you can see, there is this [2: command not found. I believe there is something wrong with my syntax however I feel like I have typed the right one.
p.s. I use Oh My ZSH to run the program. I've also tried running in VS Code however the same thing arise.
Thank you for the help.
If you put your code through shellcheck, you'll see that it is due to the lack of spaces between your variable and your bracket. Bash is a space oriented language, and [ and ] are commands just like echo or printf.
Change
elif [ $y -gt $MAX ] || [$y -lt $MIN ] || [$y -eq 0];
to
elif [ $y -gt $MAX ] || [ $y -lt $MIN ] || [ $y -eq 0 ];
^ ^ ^
Arrows added to show where spaces were added.
You can see that [ is a command if you run this at your bash prompt:
$ which [
[: shell built-in command
Because you do not have a space between [ and 2, bash assumes that you are trying to run a command called [2 and that command does not exist, as seen by your error message.
I seemed to fix it. Here is the code.
#!/bin/bash
# Given two integers X and Y. find their sum, difference, product, and quotient
# Constraints
# -100 <= X,Y <= 100
# Y != 0
# Output format
# Four lines containing the sum (X+Y), difference (X-Y), product (X x Y), and the quotient (X // Y) respectively.
# (While computing the quotient print only the integer part)
# Read data
echo "Please input for x and y!"
read x
read y
declare -i MIN=-100
declare -i MAX=100
# Checks if the valued read is in the constraints
if [ "$x" -gt $MAX ] || [ "$x" -lt $MIN ]
then
echo "Error!"
exit 1
elif [ "$y" -gt $MAX ] || [ "$y" -lt $MIN ] || [ "$y" -eq 0 ]
then
echo "Error"
exit 1
else
for operator in {"+","-","*","/",}; do echo "$x $operator $y" | bc; done
fi
There should be a space inside the [ expression ]. Pretty interesting!
what's incorrect because it is giving an error
"./vpl_execution: line 260: [: -lt: unary operator expected"
#!/bin/bash
num=16386
n=3
t11=$((10**$n))
t1=$(($num / $t11))
x=10
t2=$(($t1 / $x))
t2=$(($t2 * $x))
d=$(($t1 - $t2))
echo $d
s=0
while [ $i -lt 5 ]
do
n=$i
t11=$((10**$n))
t1=$(($num / $t11))
x=10
t2=$(($t1 / $x))
t2=$(($t2 * $x))
d1=$(($t1 - $t2))
if [ $d1 -eq $d ]
then
$s=$(( $s+1 ))
fi
done
echo $s
Variable i is not initialized before the while loop condition tests it.
Also - as #user1934428 noticed - $i is not increased anywhere.
One way to solve both issues:
while [[ ${i:=0} -lt 5 ]]; do
((i++))
I am new to Shell script. I have written a function to check weather a number is prime or not but I am getting error of Unary operator expected in if condition. Kindly point out where I have done mistake.
file :fprime.sh
prime ()
{
n=$1
t=0
for i in {2..$n}
do
r=`expr $n % $i`
if [ $r == 0 ]
then
t=`expr t + 1`
fi
done
if [ $t == 1 ]
then
echo "prime"
else
echo "not prime"
fi
}
prime
Output:
~$ ./fprime.sh 5
expr: syntax error
./fprime.sh: line 8: [: ==: unary operator expected
not prime
Try changing, 3 places there are other syntax error too,
1) if [ $r == 0 ]
// to
if [ $r -eq 0 ]
2) t=`expr t + 1`
// to
t=`expr $t + 1`
3) if [ $t == 1 ]
// to
if [ $t -eq 1 ]
Notes
1) and 3) , -eq is used to equate integers in bash == is to equate String/charactor(s)
2) - missed $ symbol for variable t
Also, just to point out, you can directly use the $1 without assigning to variable n again
The following function will return 0 (no error --> true) if the number is prime.
The function will return 1 (error --> false) if the number is not prime.
For those who never saw it before, REMAINDER_S is an array (in which every reminder of the division, different from zero, is stored).
As prime numbers are divisible only by themselves (and 1 of course), in the moment a division provides a reminder equals to zero, the number will not be prime.
Otherwise (none of the reminders is zero), the array REMINDER_S will be empty and the number will be a prime number.
Do not forget to use "break" in the first "if" statement, if you intend to substitute the "return 1" for some echo message.
is_prime () {
declare -a REMAINDER_S=()
let ARG_1=$1-1
for N in $(seq 2 $ARG_1)
do
let REMINDER=$1%$N
if [ $REMINDER == 0 ]
then
REMINDER_S=("${REMINDER_S[#]}" $REMINDER)
return 1
fi
done
if [ ${#REMINDER_S[#]} == 0 ]
then
return 0
fi
}
A simple code :
function prime()
{
for((i=2; i<=num; i++))
do
if [ `expr $num % $i` == 0 ]
then
echo $num is not prime
exit
fi
done
echo $num is a prime number
}
read num;
prime "$num";
I was trying a sample program, to check the odd and even no's and was getting an error as below,
#!/bin/bash
N=10
for i in 1..N
if [$i/2 == 0]
then
echo "even"
else
echo "Odd"
fi
Error:
./case.sh: line 5: syntax error near unexpected token `if'
./case.sh: line 5: `if [$i/2 == 0]'
EDITED :
#!/bin/bash
N=10
for i in 1..N
do
if(( ($i/2) == 0 ));
then
echo "even"
else
echo "Odd"
fi
done
error :
./case.sh: line 6: ((: (1..N/2) == 0 : syntax error: invalid arithmetic operator (error token is "..N/2) == 0 ")
Odd
Correct working code :
#!/bin/bash
N=3
for (( i=1; i <= N; i++ ));
#for i in 1..N; // This didnt work
do
if [[ $i/2 -eq 0 ]]
#if (( i/2 == 0 )); // This also worked
then
echo "even"
else
echo "Odd"
fi
done
[ ] or [[ ]] needs spaces between its arguments. And in your case you should use [[ ]] or (( )) as [ ] can't handle division along with comparison:
if [[ 'i / 2' -eq 0 ]]; then
if (( (i / 2) == 0 )); then
for i in 1..N; do should also be
for (( i = 1; i <= N; ++i )); do
You probably meant to have a form of brace expansion, but you can't apply a parameter name on it:
{1..10} ## This will work.
{1..N} ## This will not work.
Using eval may fix it but better go for the other form of for loop instead.
Try this :
#!/bin/bash
N=10
for i in $(seq 1 $N); do
if [ `expr $i % 2` -eq 0 ]
then
echo "even"
else
echo "Odd"
fi
done
1..N is not a valid syntax in bash(though I think you might be coming from ruby background), you can use seq.
I'm trying to check a number is palindromic or not. I used a function called pal(). I'm getting this error that says:
./pal.sh: line 10: [: input: integer expression expected
./pal.sh: line 23: [: input: integer expression expected
My code:
#!/bin/bash
pal()
{
num=$1
rnum=$num
add=0
k=0
while [ $num -ne 0 ]
do
lev=1
mod= $num % 10
for((i=0;i<$k;i++))
do
lev=`expr $lev \* 10`
done
mul=`expr $mod \* $lev`
add=`expr $add + $mul`
num=`expr $num / 10`
k=`expr $k + 1`
done
if [ $rnum -eq $add ]
then
echo "pallindrome"
else
echo "not pallindrome"
fi
}
echo "input number"
read input
pal input
You need to pass the value of the variable, not its name:
pal $input
Also, as anubhava points out in a comment, you will need to fix the arithmetic in the function:
mod= $num % 10
should be:
mod=$(($num % 10))
or:
((mod = $num % 10))
You should also generally avoid using expr in bash — there are built-in facilities to handle pretty much anything expr can handle.
You can do this with the rev from "util-linux" like so,
#!/bin/bash
pal()
{
input=$1
if [ $input == $(echo $input | rev) ]; then
echo "pallindrome"
else
echo "not pallindrome"
fi
}
echo "input number"
read input
pal $input
Output
$ ./pal.sh
input number
101
pallindrome
$ ./pal.sh
input number
102
not pallindrome