Values rotation in array with bash

Hello :slight_smile:

I created a little script that allow to make a rotation of values in an array. The goal was to shift the values to the right and that the last value of the array became the first value in order to create a rotation.

The purpose of the exercice was to do it without using a temporary array but to create a temporary variable in which I can put one of the values of the array then shifted all values to the right and then, put the temporary variable in the array.

Here my script :

#!/bin/bash

clear
declare -a array

read -rp " How many cases ? " box
read -rp " shift : " n
  
array=( $(seq 1 "$box"))
tmp=${array[-2]}

for((i=$tmp;i>0;i--))
do
 	array[$i]=${array[$i -1]}
done

array[0]=$tmp

for((i=0; i<$box;i++))
do
	array[$i]
done

echo " Original array : " ${array[*]}
echo " Temporary variable : " $tmp 
echo " Array shifted : ${array[*]}"

But with 2 places to shift, the result is :

Original array : 9 1 2 3 4 5 6 7 8 9
Temporary variable : 2
Array shifted : 9 1 2 3 4 5 6 7 8 9

Or I want this result :

Original array : 1 2 3 4 5 6  7 8 9 10 
Temporary variable : 2
Array shifted : 9 10 1 2 3 4 5 6 7 8

I can't set up this little script... Can you help me to solve it ?

BTS SIO/SISR - Gaston Berger

You seem to have some logical errors in your script:

  • mixing up array indices with array values (tmp being assigned a value but then used for an index).

  • using a constant (2) when the shift span (n) is given.

  • outputting the modified array twice instead of comparing the two before and after

How about

$ array=( $(seq 1 "$box"))
$ echo " Original array : " ${array
[*]}
 Original array :  1 2 3 4 5 6 7 8 9 10
$ echo " shift:" $n
 shift: 4
$ array=( $(for ((i=n; i; i--)); do echo -n  ${array[-i]}" "; unset array[-i];  done; echo ${array[@]}) )
$ echo " array shifted : " ${array
[*]}
 array shifted :  7 8 9 10 1 2 3 4 5 6
2 Likes

Comment on the previous solution: it allows echo ${array[-i]} because the =( ) splits on IFS i.e. space and newline.

If there is a cyclic shift, the % operator (modulo) is nice!
The following allows a very big (and even negative) shift:

...
# print array[ ] right-shifted by n
print_array_shifted(){
  local len=${#array[@]}
  local start=$((10*len-n))
  local end=$((start+len))
  local i
  for ((i=start; i<end; i++))
  do
    echo "${array[i%len]}"
  done
}

echo " Original array : ${array
[*]}"
array=( $(print_array_shifted) )
echo " Array shifted : ${array
[*]}"
2 Likes

Hello ! :slight_smile:

Thank you for your help !!

For me, the only problem was this line :

unset array[-i]

So I had to replace this line by :

  Unset array[$(( ${#array[*]}-1))] 

Now it works perfectly ! Thank you very much ! :slight_smile:

Hi, you should be able to shorten this to:

unset array[${#array[@]}-1]

Maybe this will help?

arr=(${arr[@]: -2} ${arr[@]:0:$((${#arr[@]}-2))})
echo ${arr[@]}
2 Likes

Very nice! Will also work with a shift variable.

And, you don't need the "Arithmetic Expansion" $((...)) within the array expansion:

echo ${arr[@]: -shift} ${arr[@]:0:${#arr[@]}-shift}
1 Like