John Shin
John Shin

Reputation: 45

Operand Expected - Shell While loop

Hello all I am trying to bubble sort in shell but I am getting an operand expected error. I have initialized the variables I need to use, and am making sure I am using the correct syntax( correct me if i didn't ). The error I am getting is on this line:

x=${array[$i]}

Thank you

#!/bin/bash

array=( "$@" )
#echo ${array[@]} print all elements in the array

if [ $# -gt 9 ]
then
    echo Too many arguments, run the program over again
    exit
fi
echo $array

i=0
j=0
#for j in { $j -lt ${#array[@]-1} }   #((j = 0; j < $@ - 1}; j++))
while [ $j -lt ${#array[@]-1} ]
do
for i in {$array}
#for((i=0; i< array; i++))
do
x=${array[$i]}
y=${array[$i]+1}
if [ $x -gt $y ]
temp=${array[$i]}
${array[$i]}=${array[$i+1]}
${array[$i+1]}=$temp
fi
done
done

echo "Sorted Array: " ${array[@]}



#if ((array[i] > array[i+1]))
    #if((x>y))
    #if [ ${array[$i]} -gt ${array[$i]+1} ] 

Upvotes: 1

Views: 553

Answers (2)

Gordon Davisson
Gordon Davisson

Reputation: 125998

There are a number of places in this script where you've got the syntax off by just enough for it to do something completely different from what you want. To demo the problems, let me initialize a couple of variables:

$ array=(15 22 30 93 27)
$ i=2

Ok, the first problem command is echo $array. This only prints the first element of the array; to print all of its elements, use echo "${array[@]}". Note that I've put double-quotes around it -- double-quotes around variable references are almost always a good idea, to avoid unexpected effects from word splitting and wildcard expansion. And when I say "good idea", I mean "if you don't do this, it'll probably work perfectly fine when you test it, and then sometime later a weird bug will show up and you'll have no idea what's going on until you track the problem down to a variable reference that should've been double-quoted". Double-quoting variable references is just good scripting hygiene. Anyway, here's a quick demo of the incorrect array printing:

$ echo $array    # wrong
15
$ echo "${array[@]}"    # right
15 22 30 93 27

Second, in while [ $j -lt ${#array[@]-1} ], that "-1" is in a place where bash doesn't do math. Generally, inside ${ }, -something means that if the variable isn't defined it should use the thing after "-" instead. Here, it basically gets ignored:

$ echo "${#array[@]-1}"    # wrong
5
$ echo "${#array[@]}"    # equivalent, still wrong
5

In order to do math, you need to get into an arithmetic context. You can do this with $(( )):

$ echo "$(( ${#array[@]} - 1 ))"    # right
4

... but in this case, it'd be simpler to use an arithmetic test expression (( )) instead of a plain test with [ ]. Note that the comparison operators are different between [ ] and (( )), and that you don't have to use $ to get variables' values; it comes out like this:

while (( j < ${#array[@]} - 1 ))    # right

Well, except that you don't increment j in the loop, so it just iterates endlessly. Either add ((j++)) inside the loop, or use a for loop instead. You have this approach in a comment, but you need to make the same syntax correction there too:

for ((j = 0; j < ${#array[@]} - 1; j++))    # also right

And actually, there's another way to do it; you can use ${!array[@]} to get a list of the indexes for the array (the "!" makes it give indexes, not contents):

for j in "${!array[@]}"    # still right

Next problem: in for i in {$array}, it just gets the first element of the array, and puts a "{ }" around it:

$ echo "{$array}"    # wrong
{15}

This is basically doing the same thing as the outer while/for loop, so any of those solutions would work here.

Next, y=${array[$i]+1} -- again, that's not an arithmetic context, so + doesn't do what you expect at all. In ${ }, +something means "use the value 'something' if the variable is defined". Since array is defined, this always sets y to "1". To solve this, just put the +1 inside the [ ] (which is an arithmetic context, so math works). Also, you can leave off the $ on i, since it's an arithmetic context and variable references are automatic:

$ echo "${array[$i]+1}"    # wrong
1
$ echo "${array[i+1]}"    # right
93

Next, for your comparison if [ $x -gt $y ], the if needs a then (either on the next line or the same line separated by a semicolon:

if [ $x -gt $y ]; then    # right

if [ $x -gt $y ]
then    # also right

BTW, the if ((array[i] > array[i+1])) you have in a comment near the end would work fine for getting and comparing the array elements. Provided you use then with it.

Ok, final problem: in ${array[$i]}=${array[$i+1]}, you're telling bash to get the value in the ith element of the array, and set the variable by that name to the value of the next element. Well, sort of, it's actually even weirder than that. Anyway, the thing is that when assigning to an array element, you don't wrap it in ${ }:

$ ${array[$i]}=${array[$i+1]}    # wrong
-bash: 30=93: command not found
$ array[$i]=${array[$i+1]}    # right
$ array[i]=${array[i+1]}    # also right

Upvotes: 2

Ken Y-N
Ken Y-N

Reputation: 15018

for i in {$array} loops by array elements, so $i is a string, not an index. To loop by index you want:

for ((i=0; i<${#array[*]}; i++));
do
    x=${array[i]}
    y=${array[i+1]}
    if [ $x -gt $y ]
        temp=${array[i]}
        ${array[i]}=${array[i+1]}
        ${array[i+1]}=$temp
    fi
done

Note that you don't need the $ sign in front of i.

Furthermore, there are built-in ways to sort; see the answers here, for instance.

Upvotes: 0

Related Questions