BASH If Command Not Found - bash

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!

Related

How to use if then in bash

We have a text file with 4 lines and a different number of words and i want to count the words per line and see if the number is even or odd but the result keeps saying
./oddwords.sh: line 14: syntax error near unexpected token `then'
./oddwords.sh: line 14: ` if[ n % 2 == 0 ]; then'
This is what ive done so far
#!/bin/bash
if [ $# -ne 1 ] ; then
{
echo "Wrong number of arguments!"
exit 1
}
fi
while read line ; do
n= echo "$(echo $line | wc -w)"
if[ n % 2 == 0 ]; then
echo "Is even"
else
echo "Is odd"
fi
done < $1
Using double brackets like
if [[ n % 2 == 0 ]]; then
and making the numbers variables at the top instead of just saying 2
a=2
b=1
at the beginning then referencing them later in the if statement like
if [[ $a%2 == 0 ]]; then
if you still have issues try
if [[ $a%2 -eq 0 ]]; then
Here are a couple sites i find useful for checking bash when I get stuck on something as well.
https://www.shellcheck.net/
https://explainshell.com/

Merge sort recursive algorithm in Bash cannot exit and return value

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

ge vs double equal in bash

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.

Shell Script command not found error

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

Shell Script Too Many Arguments for if condition

My current script does the following;
It takes integer as a command line argument and starts from 1 to N , it checks whether the numbers are divisible by 3, 5 or both of them. It simply prints out Uc for 3, Bes for 5 and UcBes for 3,5. If the command line argument is empty, it does the same operation but the loop goes to 1 to 20.
I am having this error "Too many arguments at line 11,15 and 19".
Here is the code:
#!/bin/bash
if [ ! -z $1 ]; then
for i in `seq 1 $1`
do
if [ [$i % 3] -eq 0 ]; then
echo "Uc"
elif [ i % 5 -eq 0 ]; then
echo "Bes"
elif [ i % 3 -eq 0 ] && [ i % 5 -eq 0 ]
then
echo "UcBes"
else
echo "$i"
fi
done
elif [ -z $1 ]
then
for i in {1..20}
do
if [ i % 3 -eq 0 ]
then
echo "Uc"
elif [ i % 5 -eq 0 ]
then
echo "Bes"
elif [ i % 3 -eq 0 ] && [ i % 5 -eq 0 ]
then
echo "UcBes"
else
echo "$i"
fi
done
else
echo "heheheh"
fi
Note that [ is actually synonym for the test builtin in shell (try which [ in your terminal), and not a conditional syntax like other languages, so you cannot do:
if [ [$i % 3] -eq 0 ]; then
Moreover, always make sure that there is at least one space between [, ], and the variables that comprise the logical condition check in between them.
The syntax for evaluating an expression such as modulo is enclosure by $((...)), and the variable names inside need not be prefixed by $:
remainder=$((i % 3))
if [ $remainder -eq 0 ]; then
You should probably use something like :
if [ $(($i % 3)) -eq 0 ]
instead of
if [ $i % 3 -eq 0 ]
if [ [$i % 3] -eq 0 ]
Your script could be greatly simplified. For example:
#!/bin/sh
n=0
while test $(( ++n )) -le ${1:-20}; do
t=$n
expr $n % 3 > /dev/null || { printf Uc; t=; }
expr $n % 5 > /dev/null || { printf Bes; t=; }
echo $t
done
gives slightly different error messages if the argument is not an integer, but otherwise behaves the same.

Resources