Grégory L Grégory L - 2 months ago 11
Bash Question

Beginner Shell, can't find the issue (array sorting)

Working on a little script which put random numbers in a 10 000 size array and then sort all this array with the method ask during the course.

I've done this code but it seem that it begin to sort (when I test I have some "a" that are printed but not as much as supposed to and I don't understand why)

I'm believing the problem come fromes my test on val array, and it's probably a beginner error but I don't really know how to find the problem on th web as I don't really now which line is the problem.

I don't necessary need an answer, just some clues to find it could be good :)

Here is my code: (new to stackoverflow so I don't know how to put a good code view directly, if anyone can show me)

for i in `seq 1 10000`;
do
val[${i}]=$RANDOM
done
echo `date +"%M.%S.%3N"`

FLAG=0
until [ $FLAG -eq 1 ]
do
FLAG=1
for j in `seq 1 9999`;
do
if [ ${val[${j}]} -gt ${val[${j+1}]} ]
then
TMP=${val[${j}]}
val[${j}]=${val[${j+1}]}
val[${j+1}]=$TMP
FLAG=0
echo a
fi
done
done

echo `date +"%M.%S.%3N"`


as asked I can't really have a useful output as I just want the date before and after the sort operation. But the sort is just supposed to put values from lower to higher by taking them two by two and invert them if necessary. Doing this until no numbers are inverted.

Edit: I tried with manual number:
10 3 6 9 1

when running it by putting echo ${val[*]} in the for loop it just print 4 times the same list in the same order, so I'm guessing it doesn't work at all... Is my use of "if" wrong ?

Edit 2: At the begining, I did it in C# and I wanted to do it in shell then, firstly because I wanted to practice shell and then because I wanted to compare efficiency and time needed for the same thing. here is the C# code, working.

Random random = new Random();
int[] _tab = new int[100000];
for (int i = 0; i < _tab.Length; i++)
{
_tab[i] = random.Next(1, _tab.Length);
}

bool perm;
int tmp;
DateTime dt = DateTime.Now;
do
{
perm = false;
for (int i = 0; i < (_tab.Length - 1); i++)
{
if (_tab[i] > _tab[i + 1])
{
tmp = _tab[i];
_tab[i] = _tab[i + 1];
_tab[i + 1] = tmp;
perm = true;
}
}
}
while (perm == true);

Console.WriteLine((DateTime.Now - dt).TotalMilliseconds);
Console.Read();


Thanks :)

Answer

If my understanding that you want to know why this script is not producing an "a" indicating the ordering of the array of the numbers initially produced in the "for" loop is correct, then here is a solution:

The syntax is incorrect for your variable expansion. The ${var} cannot have math operators inside the braces, because they have different meaning here. In a normal non-associative array Zsh handles subscripts with some basic math support, so you can use ${array[var+1]} instead of ${array[${var+1}]} as you previously did.

I suspect the reason this came about - complicated, error prone POSIX syntax - would have been avoided by using simplified Zsh syntax, but as stated in an earlier comment, it would not be portable to other shells.

Some shells support similar features: Bash supports most, but not bare subscripts ($array[var]). Strings may be ordered in Zsh in a similar manner, but the math-context brackets (( and )) would have to be replaced with normal test brackets [[ and ]] and the array $val might have to be defined with special typeset options to make the strings compare in the desired manner; that is, they might have to be padded and right or left aligned. For comparing enumeration types, like Jan - Feb, it gets a little more complicated with associative arrays and case-conversion.

Here is the script with the appropriate changes, then again in simplified Zsh:

#!/bin/sh
for i in `seq 1 10000`;
do
    val[$((i))]=$RANDOM
done
echo `date +"%M.%S.%3N"`

FLAG=0
until [ $FLAG -eq 1 ]
do
    FLAG=1
    for j in `seq 1 9999`;
    do
        if [ ${val[$((j))]} -gt ${val[$((j+1))]} ]
        then
            TMP=${val[$((j))]}
            val[$((j))]=${val[$((j+1))]}
            val[$((j+1))]=$TMP
            FLAG=0
            echo a
        fi
    done
done

echo `date +"%M.%S.%3N"`

Zsh:

#!/bin/zsh
foreach i ( {1..10000} )
    val[i]=$RANDOM
end
echo `date +"%M.%S.%3N"`

FLAG=0
until ((FLAG))
do
    FLAG=1
    foreach j ( {1..9999} )
        if (( val[j] > val[j+1] ))
        then
            TMP=$val[j]
            val[j]=$val[j+1]
            val[j+1]=$TMP
            FLAG=0
            echo a
        fi
    end
done

echo `date +"%M.%S.%3N"`