Hoe array in bash onliner FOR-lus omkeren?

Hoe kan ik de volgorde omkeren waarin ik een for-lus voor een gedefinieerde array uitvoer

Om de array te doorlopen doe ik dit:

$ export MYARRAY=("one" "two" "three" "four")
$ for i in ${MYARRAY[@]}; do echo $i;done
one
two
three
four

Is er een functie waarmee ik de volgorde van de array kan omkeren?

Een gedachte die ik had, was om een ​​reeks omgekeerde indexen te genereren en de elementen aan te roepen met behulp van deze omgekeerde index, maar misschien is er een sneller alternatief, of in ieder geval gemakkelijker te lezen.


Antwoord 1, autoriteit 100%

U kunt de C-stijl for loop gebruiken:

for (( idx=${#MYARRAY[@]}-1 ; idx>=0 ; idx-- )) ; do
    echo "${MYARRAY[idx]}"
done

Voor een array met “gaten” komt het aantal elementen ${#arr[@]}niet overeen met de index van het laatste element. U kunt een andere reeks indices maken en deze op dezelfde manier achteruit laten lopen:

#! /bin/bash
arr[2]=a
arr[7]=b
echo ${#arr[@]}  # only 2!!
indices=( ${!arr[@]} )
for ((i=${#indices[@]} - 1; i >= 0; i--)) ; do
    echo "${arr[indices[i]]}"
done

Antwoord 2, autoriteit 36%

Je kunt tacgebruiken, wat het tegenovergestelde is van catin de zin dat het de regels omkeert.

MYARRAY=("one" "two" "three" "four")
for item in "$MYARRAY"; do
   echo "$item"; 
done | tac
# four
# three
# two
# one

Antwoord 3, autoriteit 7%

_arr+=( '"${_arrev} is an actual "${array[@]}"' )  ⏎
_arr+=( '"${_arrev} is created as a result"' )
_arr+=( '"of reversing the key order in"' )
_arr+=( '"this "${_arr}. It handles zsh and"' )
_arr+=( '"bash arrays intelligently by tracking"' )
_arr+=( '"shell "$ENV." quotes=fine ( i hope ) "' )
. <<REVERSE /dev/stdin                    ⏎
    _arrev=( $(: $((l=${#_arr[@]}${ZSH_VERSION++1})) ; printf '"${_arr[$(('$l'-%d))]}" ' `seq 1 $l`) )
REVERSE
echo ; printf %s\\n ${_arrev}
"shell "$ENV." quotes=fine ( i hope ) "
"bash arrays intelligently by tracking"
"this "${_arr}. It handles zsh and"
"of reversing the key order in"
"${_arrev} is created as a result"
"${_arrev} is an actual "${array[@]}"

Dit zou elke mogelijke array moeten verwerken, denk ik.

Als je geïnteresseerd bent in wat daarboven gebeurt, raad ik je aan een kijkje te nemen hiereerst. Dan misschien hier, zeker hier, en, als je tijd hebt, hieren hier.

In al die antwoorden bespreek ik verschillende
aspecten van het here-document (en in vele andere)die u in uw voordeel kunt gebruiken. Ik bespreek bijvoorbeeld het tweemaal evalueren van variabelen, wat hierboven is gedaan, en in één declareer ik een functie die globaal een andere functie met de naam "_$1"in slechts 5 of 6 regels declareert – waarvan de meeste _$1() { func body ; }.Het is best handig als je het correct gebruikt.

Wat betreft het automatisch wisselen tussen bash/zsh,dat is iets anders, maar ook heel eenvoudig. Zie hier.


Antwoord 4, autoriteit 6%

Wat dacht je hiervan:

for i in `printf '%s\n' "${MYARRAY[@]}"|tac`; do echo $i; done

De uitvoer is:

four
three
two
one

Beperking: werkt niet als array nieuwe regels bevat.
Als tijdelijke oplossing kunt u een functie implementeren:

reverse(){ reversed=();local i;for ((i=$#;i>0;i--)); do reversed+=("${!i}");done; }

en gebruik het op deze manier:

reverse "${MYARRAY[@]}" && for i in "${reversed[@]}"; do echo $i; done

Antwoord 5, autoriteit 3%

Eenvoudig als een tekenreeks:

% unset c; a="1 2 3 4 5"; for b in $a; do c="$b $c"; done; echo $c

5 4 3 2 1

Weet je zeker dat je arraysyntaxis wilt??:

% unset c; declare -a c; a=(1 2 3 4 5); i=0; for b in ${a[*]}; \
    do c[$((${#a[@]}-$i))]=$b; i=$(($i+1)); done; echo ${c[*]}

5 4 3 2 1


Antwoord 6

Als je het hebt over een sequentiële numerieke array (bijvoorbeeld om bash-geschiedenis te verwijderen), kun je dit bijvoorbeeld in omgekeerde volgorde weergeven:

for i in {16..10}; do history -d $i; done

Ik realiseer me dat dit een beetje off-topic is en mijn excuses daarvoor, maar ik dacht dat het waarschijnlijk het vermelden waard was.


Antwoord 7

Ik zou adviseren om het gebruik van one-liners voor dergelijke dingen te beperken en in plaats daarvan een functie te schrijven die afkomstig is uit je shell of script. Hier is een voorbeeld voor nieuwere versies van Bash waar het mogelijk is om meerdere arrays door te geven door te verwijzen naar een functie…

reverse_array(){
  local -n _source_array_ref="${1}"
  local -n _destination_array_ref="${2}"
  for ((_index=${#_source_array_ref[@]}-1; _index>=0; _index--)); do
    _destination_array_ref+=("${_source_array_ref[$_index]}")
  done
}
_list=(spam ham space)
_new_list=()
reverse_array '_list' '_new_list'
printf '%s\n' "${_new_list[@]}"
#> space
#> ham
#> spam

… houd er echter rekening mee dat deze techniek een functie maakt die onzuiveris (bijwerkingen heeft), wat inhoudt dat het debuggen van _listof _new_listkan moeilijk worden als je in een Bash-scripting-mentaliteit zit… ook huidige voorbeelden houden er geen rekening mee dat men reverse_arrayopnieuw zou kunnen uitvoeren en eindigen met _new_listtoegevoegd aan meerdere keren; wat al dan niet gewenst is.


Antwoord 8

u kunt ook overwegen om seq

. te gebruiken

MYARRAY=("one" "two" "three" "four")
for i in $(seq $((${#MYARRAY[@]} - 1)) -1 0); do
    echo ${MYARRAY[$i]}
done

in freebsd kunt u de parameter -1 increment weglaten:

for i in $(seq $((${#MYARRAY[@]} - 1)) 0); do
    echo ${MYARRAY[$i]}
done

Other episodes