Existe-t-il un moyen simple d'inverser un tableau?
#!/bin/bash
array=(1 2 3 4 5 6 7)
echo "${array[@]}"
donc j'obtiendrais: 7 6 5 4 3 2 1
au lieu de: 1 2 3 4 5 6 7
J'ai répondu à la question telle qu'elle est écrite, et ce code inverse le tableau. (L'impression des éléments dans l'ordre inverse sans inverser le tableau est juste une boucle for
décomptant du dernier élément à zéro.) Il s'agit d'un algorithme standard de "permutation du premier et du dernier".
array=(1 2 3 4 5 6 7)
min=0
max=$(( ${#array[@]} -1 ))
while [[ min -lt max ]]
do
# Swap current first and last elements
x="${array[$min]}"
array[$min]="${array[$max]}"
array[$max]="$x"
# Move closer
(( min++, max-- ))
done
echo "${array[@]}"
Cela fonctionne pour les tableaux de longueur paire et impaire.
Une autre approche non conventionnelle:
#!/bin/bash
array=(1 2 3 4 5 6 7)
f() { array=("${BASH_ARGV[@]}"); }
shopt -s extdebug
f "${array[@]}"
shopt -u extdebug
echo "${array[@]}"
Production:
7 6 5 4 3 2 1
Si extdebug
est activé, le tableau BASH_ARGV
contient dans une fonction tous les paramètres positionnels dans l'ordre inverse.
Approche non conventionnelle (toutes non pures bash
):
si tous les éléments d'un tableau sont composés d'un seul caractère (comme dans la question), vous pouvez utiliser rev
:
echo "${array[@]}" | rev
autrement:
printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
et si vous pouvez utiliser zsh
:
echo ${(Oa)array}
Pour échanger les positions de tableau en place (même avec des tableaux clairsemés) (depuis bash 3.0):
#!/bin/bash
# Declare an sparse array to test:
array=([5]=101 [6]=202 [10]=303 [11]=404 [20]=505 [21]=606 [40]=707)
echo "Initial array values"
declare -p array
swaparray(){ local temp; temp="${array[$1]}"
array[$1]="${array[$2]}"
array[$2]="$temp"
}
ind=("${!array[@]}") # non-sparse array of indexes.
min=-1; max="${#ind[@]}" # limits to one before real limits.
while [[ min++ -lt max-- ]] # move closer on each loop.
do
swaparray "${ind[min]}" "${ind[max]}" # Exchange first and last
done
echo "Final Array swapped in place"
declare -p array
echo "Final Array values"
echo "${array[@]}"
À l'exécution:
./script
Initial array values
declare -a array=([5]="101" [6]="202" [10]="303" [11]="404" [20]="505" [21]="606" [40]="707")
Final Array swapped in place
declare -a array=([5]="707" [6]="606" [10]="505" [11]="404" [20]="303" [21]="202" [40]="101")
Final Array values
707 606 505 404 303 202 101
Pour les anciens bash, vous devez utiliser une boucle (en bash (depuis 2.04)) et utiliser $a
pour éviter l'espace de fuite:
#!/bin/bash
array=(101 202 303 404 505 606 707)
last=${#array[@]}
a=""
for (( i=last-1 ; i>=0 ; i-- ));do
printf '%s%s' "$a" "${array[i]}"
a=" "
done
echo
Pour bash depuis 2.03:
#!/bin/bash
array=(101 202 303 404 505 606 707)
last=${#array[@]}
a="";i=0
while [[ last -ge $((i+=1)) ]]; do
printf '%s%s' "$a" "${array[ last-i ]}"
a=" "
done
echo
Aussi (en utilisant l'opérateur de négation au niveau du bit) (depuis bash 4.2+):
#!/bin/bash
array=(101 202 303 404 505 606 707)
last=${#array[@]}
a=""
for (( i=0 ; i<last ; i++ )); do
printf '%s%s' "$a" "${array[~i]}"
a=" "
done
echo
Si vous voulez réellement l'inverse dans un autre tableau:
reverse() {
# first argument is the array to reverse
# second is the output array
declare -n arr="$1" rev="$2"
for i in "${arr[@]}"
do
rev=("$i" "${rev[@]}")
done
}
Alors:
array=(1 2 3 4)
reverse array foo
echo "${foo[@]}"
Donne:
4 3 2 1
Cela devrait gérer correctement les cas où un index de tableau est manquant, disons que vous aviez array=([1]=1 [2]=2 [4]=4)
, auquel cas une boucle de 0 à l'index le plus élevé peut ajouter des éléments supplémentaires vides.
Moche, impossible à entretenir, mais à une ligne:
eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"
La solution Pure Bash fonctionnerait comme une doublure.
$: for (( i=${#array[@]}-1; i>=0; i-- ))
> do rev[${#rev[@]}]=${array[i]}
> done
$: echo "${rev[@]}"
7 6 5 4 3 2 1
Pour inverser un tableau arbitraire (qui peut contenir n'importe quel nombre d'éléments avec n'importe quelle valeur):
Avec zsh
:
array_reversed=("${(@Oa)array}")
Avec bash
4.4+, étant donné que les variables bash
ne peuvent de toute façon pas contenir d'octets NUL, vous pouvez utiliser GNU tac -s ''
sur les éléments imprimés en tant qu'enregistrements délimités NUL:
readarray -td '' array_reversed < <(
((${#array[@]})) && printf '%s\0' "${array[@]}" | tac -s '')
POSIXly, pour inverser le tableau Shell POSIX ($@
, fait de $1
, $2
...):
code='set --'
n=$#
while [ "$n" -gt 0 ]; do
code="$code \"\${$n}\""
n=$((n - 1))
done
eval "$code"
Bien que je ne vais pas dire quelque chose de nouveau et que j'utiliserai également tac
pour inverser le tableau, je pense que cela vaut la peine de mentionner une solution à une seule ligne en utilisant la version 4.4 de bash:
$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}" |tac)
Essai:
$ array=(1 2 3 4 5 6 10 11 12)
$ echo "${array[@]}"
1 2 3 4 5 6 10 11 12
$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}"|tac)
$ echo "${array[@]}"
12 11 10 6 5 4 3 2 1
Gardez à l'esprit que le nom var à l'intérieur de read est le nom du tableau d'origine, donc aucun tableau d'assistance n'est requis pour le stockage temporaire.
Mise en œuvre alternative en ajustant IFS:
$ IFS=$'\n' read -d '' -a array < <(printf '%s\n' "${array[@]}"|tac);declare -p array
declare -a array=([0]="12" [1]="11" [2]="10" [3]="6" [4]="5" [5]="4" [6]="3" [7]="2" [8]="1")
PS: Je pense que les solutions ci-dessus ne fonctionneront pas dans bash
version ci-dessous 4.4
en raison de l'implémentation de la fonction intégrée bash read
.