check if a number is a prime in bash - linux

I am trying to write a bash script to find if a number is prime, but i can't find what is wrong with my script
#!/bin/bash
#set -x
echo -n "enter a number "
read isPrime
count=2
x=0
while [ $count -lt $isPrime ]; do
if [ `expr $isPrime % $count`-eq 0 ]; then
echo "not prime"
fi
count=`expr $count + 1`
done
echo " it is prime"
#set +x

Using factor would be easy. But if you somehow need a script, I would implement something like following. I'm not sure whether this is the best algorithm, but this is way efficient than yours.
function is_prime(){
if [[ $1 -eq 2 ]] || [[ $1 -eq 3 ]]; then
return 1 # prime
fi
if [[ $(($1 % 2)) -eq 0 ]] || [[ $(($1 % 3)) -eq 0 ]]; then
return 0 # not a prime
fi
i=5; w=2
while [[ $((i * i)) -le $1 ]]; do
if [[ $(($1 % i)) -eq 0 ]]; then
return 0 # not a prime
fi
i=$((i + w))
w=$((6 - w))
done
return 1 # prime
}
# sample usage
is_prime 7
if [[ $? -eq 0 ]]; then
echo "not a prime"
else
echo "it's a prime"
fi
You can find an explanation about the used algorithm here

Related

Bash comparing float numbers using POSIX compliance

my case is very simple:
I am trying to compare two values:
a=0.2
b=0.1
The code that I am trying to execute is:
if [ "$a" -gt "$b" ]; then
echo "You are running an outdated version"
fi
Assuming you want to compare version numbers, would you please try the following:
#!/bin/bash -posix
# compares version numbers
# prints 0 if $a == $b
# positive number if $a is newer than $b
# negative number if $a is older than $b
vercmp() {
local a=$1
local b=$2
local a1=${a%%.*} # major number of $a
local b1=${b%%.*} # major number of $b
if [[ $a = "" ]]; then
if [[ $b = "" ]]; then
echo 0 # both $a and $b are empty
else
vercmp "0" "$b"
fi
elif [[ $b = "" ]]; then
vercmp "$a" "0"
elif (( 10#$a1 == 10#$b1 )); then
local a2=${a#*.} # numbers after the 1st dot
if [[ $a2 = $a ]]; then
a2="" # no more version numbers
fi
local b2=${b#*.} # numbers after the 1st dot
if [[ $b2 = $b ]]; then
b2="" # no more version numbers
fi
vercmp "$a2" "$b2"
else
echo $(( 10#$a1 - 10#$b1 ))
fi
}
Examples:
vercmp 0.2 0.1
=> 1 (positive number: the former is newer)
vercmp 1.0.2 1.0.10
=> -8 (negative number: the latter is newer)
a=0.2
b=0.1
if (( $(vercmp "$a" "$b") > 0 )); then
echo "You are running an outdated version"
fi

Multiple conditions in case statement, bash

read X;
read Y;
read Z;
if [[ $X,$Y,$Z -ne "0" ]]; then
if [[ $X,$Y,$Z -ge 1 && $X,$Y,$Z -le 1000 ]] && [[ $((X+Y)) -gt $Z || $((Y+Z)) -gt $X || $((X+Z)) -gt $Y ]]; then
case $X,$Y,$Z in
$X -eq $Y && $Y -eq $Z && $X -eq $Z ) echo "EQUILATERAL";;
esac
else
echo "bye";
fi
fi
*./bashtesting.sh: line 7: syntax error near unexpected token `-eq
./bashtesting.sh: line 7: $X -eq $Y && $Y -eq $Z && $X -eq $Z ) echo "EQUILATERAL";;*
how to equate the three variables at a time?
This is how I would do it, with a loop and associative array.
#!/usr/bin/env bash
for h in x y z; do
read -rp 'Enter input: ' input
if [[ -z $input ]]; then
printf >&2 '%s is empty, please try again!\n' "${input:-input}"
exit 1
elif [[ $input != *[0-9]* ]]; then
printf '%s is not an int, please try again!\n' "$input"
exit 1
else
declare -A num[$h]=$input
fi
done
for i in "${num[#]}"; do
if ! (( i == 0 )); then
for j in "${num[#]}"; do
if (( j > 1 && j < 100 )); then
if (( (num[x] + num[y]) > num[z] || (num[y] + num[z]) > num[x] || (num[x] + num[z]) > num[y] )); then
if (( num[x] == num[y] && num[y] == num[x] && num[x] == num[z] )); then
echo equilateral
break 2
fi
fi
fi
done
fi
done
It it not pretty, It looks like an Anti Pattern, and there might be a better way to do it but this will get you there I suppose.

If statement 3 conditions Bash

I want to do an if statement with three conditions that have to be satisfied at the same time. I am using Ubuntu Bash for Windows and the values $c1, $c2 and $c3 are non-integer (decimal negative numbers).
if [ (( $(echo "$c1 < 0" | bc -l) )) ] && [ (( $(echo "$c2 < 0" | bc -l) )) ] && [ (( $(echo "$c3 < 0" | bc -l) )) ];
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
However, I get the following syntax error in the if line: syntax error near unexpected token `('
If I just put one condition:
if (( $(echo "$c1 < 0" | bc -l) ));
it works, but when I add the three of them as AND (&&), I get the error. Can anyone help me?
Considerably more efficient (assuming you know your values are numbers, and only need to check whether they're all negative) would be:
if [[ $c1 = -* ]] && [[ $c2 = -* ]] && [[ $c3 = -* ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
If you want to be more specific about the permissible formats (f/e, allowing leading spaces), a regex is another option, which similarly can be implemented more efficiently than spawning a series of subshells invoking bc:
nnum_re='^[[:space:]]*-([[:digit:]]*[.][[:digit:]]+|[[:digit:]]+)$'
if [[ $c1 =~ $nnum_re ]] && [[ $c2 =~ $nnum_re ]] && [[ $c3 =~ $nnum_re ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
First, pass the relational AND operators into bc to get rid of some punctuation (also only invokes bc once):
if (( $(echo "$c1 < 0 && $c2 < 0 && $c3 < 0" | bc -l) == 1 ))
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
Although if it were me, I would create a shell function returning a "true" exit status if bc evaluates the result of an expression to non-zero. Then you can hide most of the ugly punctuation in one place separated from your main logic:
function bc_true() {
(( $(echo "$#" | bc -l) != 0 ))
}
And write a (IMO) cleaner shell expression:
if bc_true "$c1 < 0 && $c2 < 0 && $c3 < 0"
then
...

linux syntax error-unexpected token near "r"

I am writing a couple of bash scripts which I wish to call within another bash script but I can't seem to get these two examples online to work. It keeps telling me that there is an unexpected error near "r" and one near "mychoice" though it doesn't get resolved when I make the changes. The two classes are to generate a prime numbers test which if it works, I hope to be able to stress a cpu. the other class is just to dynamically select how long to run sar rather than having to hard code in sar 1 30 for example. Does anyone know what is wrong with these classes? Thanks in advance.
primetest class
#!/bin/bash
#
# primes.sh - find all prime numbers up to a certain number
# 2008 - Mike Golvach - eggi#comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
factorial () {
local factorial_count=$1
if [ "$factorial_count" -eq 0 ]
then
factorial_count=1
fi
for (( factor=$((factorial_count -1)); $factor >= 1; --factor ))
do
factorial_count=$(($factorial_count * $factor))
done
echo $factorial_count
}
prime_number () {
local prime=$1
p_minus_1=$(($prime - 1))
fact_p_minus_1=`factorial "$p_minus_1"`
fact_plus_1=$(($fact_p_minus_1 + 1))
echo $fact_plus_1
}
highest_number=$1
if [ -z $highest_number ]
then
echo
echo "Usage: $0 highestNumber"
echo
exit 1
fi
if [ $highest_number -eq 0 ]
then
echo
echo "Sorry. 0 is not a prime number"
echo
exit 0
elif [ $highest_number -eq 1 ]
then
echo
echo "Sorry. 0 and 1 are not prime numbers"
echo
exit 0
fi
echo "Generating Prime Numbers Up To $highest_number"
if [ $highest_number -eq 2 ]
then
echo
echo -n "2"
else
echo
echo -n "2 3 "
fi
count=4
while [ $count -le $highest_number ]
do
prime_return=`prime_number "$count"`
prime_test=$(($prime_return % count))
if [ $prime_test -eq 0 ]
then
echo -n "$count "
fi
count=$(($count + 1))
done
echo
echo
echo "All Set!"
echo
exit 0

When/how to use "==" or "-eq" operator in test?

In the following code I want to compare the command line arguments with the parameters but I am not sure what is the current syntax to compare the arguments with parameters..i.e "==" or "-eq".
#!/bin/bash
argLength=$#
#echo "arg = $1"
if [ argLength==0 ]; then
#Running for the very first
#Get the connected device ids and save it in an array
N=0
CONNECTED_DEVICES=$(adb devices | grep -o '\b[A-Za-z0-9]\{8,\}\b'|sed -n '2,$p')
NO_OF_DEVICES=$(echo "$CONNECTED_DEVICES" | wc -l)
for CONNECTED_DEVICE in $CONNECTED_DEVICES ; do
DEVICE_IDS[$N]="$CONNECTED_DEVICE"
echo "DEVICE_IDS[$N]= $CONNECTED_DEVICE"
let "N= $N + 1"
done
for SEND_DEVICE_ID in ${DEVICE_IDS[#]} ; do
callCloneBuildInstall $SEND_DEVICE_ID
done
elif [ "$1" -eq -b ]; then
if [ $5 -eq pass ]; then
DEVICE_ID=$3
./MonkeyTests.sh -d $DEVICE_ID
else
sleep 1h
callCloneBuildInstall $SEND_DEVICE_ID
fi
elif [ "$1" -eq -m ]; then
echo "Check for CloneBuildInstall"
if [ "$5" -eq pass ]; then
DEVICE_ID=$3
callCloneBuildInstall $SEND_DEVICE_ID
else
echo "call CloneBuildInstall"
# Zip log file and save it with deviceId
callCloneBuildInstall $SEND_DEVICE_ID
fi
fi
function callCloneBuildInstall {
./CloneBuildInstall.sh -d $SEND_DEVICE_ID
}
From help test:
[...]
STRING1 = STRING2
True if the strings are equal.
[...]
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
But in any case, each part of the condition is a separate argument to [.
if [ "$arg" -eq 0 ]; then
if [ "$arg" = 0 ]; then
Why not use something like
if [ "$#" -ne 0 ]; then # number of args should not be zero
echo "USAGE: "
fi
When/how to use “==” or “-eq” operator in test?
To put it simply use == when doing lexical comparisons a.k.a string comparisons but use -eq when having numerical comparisons.
Other forms of -eq (equal) are -ne (not equal), -gt (greater than), -ge (greater than or equal), -lt (lesser than), and -le (lesser than or equal).
Some may also suggest preferring (( )).
Examples:
[[ $string == "something else" ]]
[[ $string != "something else" ]] # (negated)
[[ $num -eq 1 ]]
[[ $num -ge 2 ]]
(( $num == 1 ))
(( $num >= 1 ))
And always use [[ ]] over [ ] when you're in Bash since the former skips unnecessary expansions not related to conditional expressions like word splitting and pathname expansion.

Resources