Réponses:
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 for
boucle à rebours 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 ne sont qu'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}
tac
, comme le contraire de cat
très bon à retenir, MERCI!
rev
, je dois mentionner que rev
cela ne fonctionnera pas correctement pour les numéros à deux chiffres. Par exemple, un élément de tableau 12
utilisant rev sera imprimé sous la forme 21
. Essayez-le ;-)
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, par exemple array=([1]=1 [2]=2 [4]=4)
, auquel cas une boucle de 0 à l'index le plus élevé peut ajouter des éléments vides supplémentaires.
shellcheck
imprime deux avertissements: array=(1 2 3 4)
<-- SC2034: array appears unused. Verify it or export it.
et:echo "${foo[@]}"
<-- SC2154: foo is referenced but not assigned.
declare
ligne.
declare -n
ne semble pas fonctionner dans les versions bash antérieures à 4.3.
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 bash plus anciens, vous devez utiliser une boucle (dans bash (depuis 2.04)) et utiliser $a
pour éviter l'espace de fin:
#!/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
Moche, impossible à entretenir, mais à une ligne:
eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"
eval eval echo "'\"\${array[-'{1..${#array[@]}}']}\"'"
.
ind=("${!array[@]}");eval eval echo "'\"\${array[ind[-'{1..${#array[@]}}']]}\"'"
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 vaudrait 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 la bash
version ci-dessous en 4.4
raison de la read
mise en œuvre de la fonction intégrée bash différente .
IFS
version fonctionne , mais il est aussi l' impression: declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="10" [7]="11" [8]="12")
. Utilisation de bash 4.4-5
. Vous devez retirer ;declare -p array
à la fin de la première ligne, alors ça marche ...
declare -p
n'est qu'un moyen rapide de faire en sorte que bash imprime le vrai tableau (index et contenu). Vous n'avez pas besoin de cette declare -p
commande dans votre vrai script. Si quelque chose ne va pas dans vos affectations de tableaux, vous pourriez vous retrouver dans un cas qui ${array[0]}="1 2 3 4 5 6 10 11 12"
= toutes les valeurs stockées dans le même index - en utilisant l'écho, vous ne verrez aucune différence. Pour une impression rapide du tableau à l'aide de declare -p array
, vous retournerez les véritables indecs du tableau et la valeur correspondante dans chaque index.
read -d'\n'
méthode n'a pas fonctionné pour vous?
read -d'\n'
fonctionne bien.
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 bash
variables 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 '')
POSIX, pour inverser le tableau shell POSIX ( $@
, composé de $1
, $2
...):
code='set --'
n=$#
while [ "$n" -gt 0 ]; do
code="$code \"\${$n}\""
n=$((n - 1))
done
eval "$code"
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
rev+=( "${array[i]}" )
semble plus simple.
vous pouvez également envisager d'utiliser seq
array=(1 2 3 4 5 6 7)
for i in $(seq $((${#array[@]} - 1)) -1 0); do
echo ${array[$i]}
done
dans freebsd, vous pouvez omettre le paramètre d'incrémentation -1:
for i in $(seq $((${#array[@]} - 1)) 0); do
echo ${array[$i]}
done
array=(1 2 3 4 5 6 7)
echo "${array[@]} " | tac -s ' '
Ou
array=(1 2 3 4 5 6 7)
reverse=$(echo "${array[@]} " | tac -s ' ')
echo ${reverse[@]}
7 6 5 4 3 2 1
$ tac --version
tac (GNU coreutils) 8.28