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";
Related
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.
I'm implementing a merge sort algorithm in bash, but looks like it loops forever and gives error on m1 and m2 subarrays. It's a bit hard to stop loop in conditions since I have to use echo and not return. Anyone have any idea why this happens?
MergeSort (){
local a=("$#")
if [ ${#a[#]} -eq 1 ]
then
echo ${a[#]}
elif [ ${#a[#]} -eq 2 ]
then
if [ ${a[0]} -gt ${a[1]} ]
then
local t=(${a[0]} ${a[1]})
echo ${t[#]}
else
echo ${a[#]}
fi
else
local p=($(( ${#a[#]} / 2 )))
local m1=$(MergeSort "${a[#]::p}")
local m2=$(MergeSort "${a[#]:p}")
local ret=()
while true
do
if [ "${#m1[#]}" > 0 ] && [ "${#m2[#]}" > 0 ]
then
if [ ${m1[0]} <= ${m2[0]} ]
then
ret+=(${m1[0]})
m1=${m1[#]:1}
else
ret+=(${m2[0]})
m2=${m2[#]:1}
fi
elif [ ${#m1[#]} > 0 ]
then
ret+=(${ret[#]} ${m1[#]})
unset m1
elif [ ${#m2[#]} > 0 ]
then
ret+=(${ret[#]} ${m2[#]})
unset m2
else
break
fi
done
fi
echo ${ret[#]}
}
a=(6 5 6 4 2)
b=$(MergeSort "${a[#]}")
echo ${b[#]}
There are multiple issues in your shell script:
you should use -gt instead of > for numeric comparisons on array lengths
<= is not a supported string comparison operator. You should use < and quote it as '<', or better use '>' and transpose actions to preserve sort stability.
there is no need for local t, and your code does not swap the arguments. Just use echo ${a[1]} ${a[0]}
you must parse the result of recursive calls to MergeSort as arrays: local m1=($(MergeSort "${a[#]::p}"))
when popping initial elements from m1 and m2, you must reparse as arrays: m1=(${m1[#]:1})
instead of ret+=(${ret[#]} ${m1[#]}) you should just append the elements with ret+=(${m1[#]}) and instead of unset m1, you should break from the loop. As a matter of fact, if either array is empty you should just append the remaining elements from both arrays and break.
furthermore, the while true loop should be simplified as a while [ ${#m1[#]} -gt 0 ] && [ ${#m2[#]} -gt 0 ] loop followed by the tail handling.
the final echo ${ret[#]} should be moved inside the else branch of the last if
to handle embedded spaces, you should stringize all expansions but as the resulting array is expanded with echo embedded spaces that appear in the output are indistinguishable from word breaks. There is no easy workaround for this limitation.
Here is a modified version:
#!/bin/bash
MergeSort (){
local a=("$#")
if [ ${#a[#]} -eq 1 ]; then
echo "${a[#]}"
elif [ ${#a[#]} -eq 2 ]; then
if [ "${a[0]}" '>' "${a[1]}" ]; then
echo "${a[1]}" "${a[0]}"
else
echo "${a[#]}"
fi
else
local p=($(( ${#a[#]} / 2 )))
local m1=($(MergeSort "${a[#]::p}"))
local m2=($(MergeSort "${a[#]:p}"))
local ret=()
while [ ${#m1[#]} -gt 0 ] && [ ${#m2[#]} -gt 0 ]; do
if [ "${m1[0]}" '>' "${m2[0]}" ]; then
ret+=("${m2[0]}")
m2=("${m2[#]:1}")
else
ret+=("${m1[0]}")
m1=("${m1[#]:1}")
fi
done
echo "${ret[#]}" "${m1[#]}" "${m2[#]}"
fi
}
a=(6 5 6 4 2 a c b c aa 00 0 000)
b=($(MergeSort "${a[#]}"))
echo "${b[#]}"
Output: 0 00 000 2 4 5 6 6 a aa b c c
I am trying to solve a hackerrank exercise.
If n is odd, print Weird
If n is even and in the inclusive range of 2 to 5, print Not Weird
If n is even and in the inclusive range of 6 to 20, print Weird
If n is even and greater than 20, print Not Weird
My code is as follows:
read n
if [ $n%2==0 ]; then
if [ $n -ge 6 ] && [ $n -le 20 ]; then
echo "Weird"
else
echo "Not Weird"
fi
else
echo "Weird"
fi
When I give the input as 3, the result I get is Not Weird which is not correct same for 1 I get Not Weird. However, when I try this:
read n
if [ $(($n%2)) -eq 0 ]; then
if [ $n -ge 6 ] && [ $n -le 20 ]; then
echo "Weird"
else
echo "Not Weird"
fi
else
echo "Weird"
fi
I get the right result. What is the difference?
[ ] (or test) builtin:
==, or to be POSIX compliant =, does a string comparison
-eq does a numeric comparison
Note: == and -eq (and other comparisons) are parameters to the [ command, so they must be separated by whitespace, so $n%2==0 is invalid.
[[ ]] keyword:
is as [ except that it does pattern matching. Being a keyword rather than a builtin, expansion with [[ is done earlier in the scan.
(( )) syntax
Carries out arithmetic evaluation as with the let builtin. Whitespace separators are not mandatory. Using a leading $ to expand a variable is not necessary and is not recommended since it changes the expansion order.
For truth evaluation inside if-else, bash provides ((..)) operators with no need of a $ on the front.
n=5
if (( (n % 2) == 0 )); then
echo "Something"
if (( n >= 6 )) && (( n <= 20 )); then
echo "Some other thing"
else
echo "Other else thing"
fi
else
echo "Something else"
fi
Read here for more information.
Below my script for up to n prime numbers. When I run it, it always shows an error that command not found in line 12 and 18 both. What am I doing wrong?
clear
echo"enter the number upto which you want prime numbers : "
read n
for((i=1;i<=n;i++))
do
flag=0
for((j=2;j<i;j++))
do
if [expr $i % $j-eq 0]
then
flag=1
fi
done
if [$flag-eq 0]
then
echo $i
fi
done
As pointed out in comments, you must use spaces around [ and ], as well as the comparison operators. Even more safe when using [ and ] is quoting your variables to avoid word splitting (not actually required in this specific case, though).
Additionally, you want to compare the output of expr to 0, so you have to use command substitution:
if [ $(expr "$i" % "$j") -eq 0 ]
and
if [ "$flag" -eq 0 ]
Since you're using Bash, you can use the (( )) compound command:
if (( i % j == 0 ))
and
if (( flag == 0 ))
No expr needed, no command substitution, no quoting required, no $ required, and the comparison operators have their "normal", expected meaning.
There are a number of syntax errors other than the brackets of if statement. Kindly go through the piece of code below. I have checked it running on my system.
#!/bin/sh
echo "enter the number upto which you want prime numbers : "
read n
for((i=1;i<=n;i++))
do
flag=0
for((j=2;j<i;j++))
do
if [ `expr $i % $j` -eq 0 ]
then flag=1
fi
done
if [ $flag -eq 0 ]
then echo $i
fi
done
I want to parse a list of hexas that i get from a file after parsing them for unique hexas and sorting them.
The list looks like this:
1 0xb6e38000
8 0xb6f66000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
2 0xb6ff8000
4 0xb6ffa000
1 0xb6ffb000
Now what i want to do, is refine it even more so that i get only the hexas with prime numbers in front, like this:
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
The command i have been using is this:
sort | uniq -c | grep " 1 0x"
But this command lists only the ones that appear only once in the file.
Can anyone help me "sort" this out ?
Original answer for prime numbers
As mentioned in the comments to the question, 1 is not prime, but in the description of desired output you list the lines starting with 1. However, if you actually want to filter out the lines with prime numbers in the first column, then the following script will help:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
function is_prime {
declare -i n="$1"
if [ $n -le 1 ]; then
return 1
elif [ $n -le 3 ]; then
return 0
elif [[ $(( $n % 2 )) == 0 || $(( $n % 3 )) == 0 ]]; then
return 1
fi
i=5
while [[ $(( $i * $i )) -le $n ]]; do
if [[ $(( $n % $i )) == 0 || $(( $n % ($i + 2) )) == 0 ]]; then
return 1
fi
(( i += 6 ))
done
return 0
}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
if is_prime ${line[0]}; then
echo $line
else
echo >&2 "skipping ${line[*]} as ${line[0]} is not prime"
fi
done < "$source_file"
In this script we define is_prime function which returns zero (success status), if the first argument ($1) is a prime number. Non-zero means non-prime number. The algorithm is a version of this pseudo-code translated into Bash.
Then we read $source_file line by line with the while loop where we put the columns into the line array: read -a line. Then we check if is_prime ${line[0]} command exits with a success code (zero) and output the line if it is. Otherwise, we print a message to the standard error (echo >&2).
The script accepts optional argument for the source file path. It assigns $source_file to /tmp/source-file, if the first argument is missing.
Script usage
Save the above-mentioned code in script.sh file.
Call bash script.sh /path/to/source-file >filtered 2>errors
Examine the contents of filtered and errors files. The first will contain the lines filtered out from the source file.
The output files will look like the following:
filtered
5 0xb6f69000
3 0xb6fd4000
2 0xb6ff8000
errors
skipping 1 0xb6e38000 as 1 is not prime
skipping 8 0xb6f66000 as 8 is not prime
skipping 1 0xb6f6c000 as 1 is not prime
skipping 1 0xb6ff7000 as 1 is not prime
skipping 4 0xb6ffa000 as 4 is not prime
skipping 1 0xb6ffb000 as 1 is not prime
Update for odd numbers
i sould have mentioned this from the start, i need odd number lines to
show up, not prime. My bad. – biotic
It's easy to detect if a number is odd with an expression like if [[ $(( $n % 2 )) != 0 ]]. The expression checks if the remainder by 2 is not equal to zero, i.e. applies the modulo operation. If the remainder is zero, then the number is even, otherwise odd, of course.
The full script:
#!/bin/bash -
[ $# -gt 0 ] && source_file="$1"
: ${source_file:=/tmp/source-file}
while read -a line; do
# We accept only two or more columns
[ ${#line[#]} -ge 2 ] || continue;
declare -i n=${line[0]}
if [[ $(( $n % 2 )) != 0 ]]; then
echo ${line[*]}
else
echo >&2 "skipping ${line[*]} as ${line[0]} is even"
fi
done < "$source_file"
As for above, you run bash script.sh /path/to/source-file >filtered 2>errors
Sample output:
filtered
1 0xb6e38000
5 0xb6f69000
1 0xb6f6c000
3 0xb6fd4000
1 0xb6ff7000
1 0xb6ffb000
errors
skipping 8 0xb6f66000 as 8 is even
skipping 2 0xb6ff8000 as 2 is even
skipping 4 0xb6ffa000 as 4 is even