Respuestas:
He respondido la pregunta tal como está escrita, y este código invierte la matriz. (Imprimir los elementos en orden inverso sin invertir la matriz es solo un forciclo de cuenta regresiva desde el último elemento a cero). Este es un algoritmo estándar de "intercambio primero y último".
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[@]}"
Funciona para matrices de longitud impar y par.
Otro enfoque poco convencional:
#!/bin/bash
array=(1 2 3 4 5 6 7)
f() { array=("${BASH_ARGV[@]}"); }
shopt -s extdebug
f "${array[@]}"
shopt -u extdebug
echo "${array[@]}"
Salida:
7 6 5 4 3 2 1
Si extdebugestá habilitado, la matriz BASH_ARGVcontiene en una función todos los parámetros posicionales en orden inverso.
Enfoque no convencional (todo no puro bash):
si todos los elementos en una matriz son solo un carácter (como en la pregunta) puede usar rev:
echo "${array[@]}" | revde otra manera:
printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echoy si puedes usar zsh:
echo ${(Oa)array}tac, como lo opuesto a catbastante bueno para recordar, ¡GRACIAS!
rev, necesito mencionar que revno funcionará correctamente para números con dos dígitos. Por ejemplo, un elemento de matriz de 12 usar rev se imprimirá como 21. Pruébalo ;-)
Si realmente quieres lo contrario en otra matriz:
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
}
Luego:
array=(1 2 3 4)
reverse array foo
echo "${foo[@]}"
Da:
4 3 2 1
Esto debería manejar correctamente los casos en los que falta un índice de matriz, digamos que tenía array=([1]=1 [2]=2 [4]=4), en cuyo caso el bucle de 0 al índice más alto puede agregar elementos adicionales vacíos.
shellcheckimprime dos advertencias: array=(1 2 3 4) <-- SC2034: array appears unused. Verify it or export it.y para:echo "${foo[@]}" <-- SC2154: foo is referenced but not assigned.
declarelínea.
declare -nparece no funcionar en las versiones bash anteriores a 4.3.
Para intercambiar las posiciones de la matriz en su lugar (incluso con matrices dispersas) (desde 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[@]}"
En ejecución:
./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
Para bash más antiguo, debe usar un bucle (en bash (desde 2.04)) y usar $apara evitar el espacio final:
#!/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
Para bash desde 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
Además (usando el operador de negación bit a bit) (desde 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
Feo, imposible de mantener, pero de una sola línea:
eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"
eval eval echo "'\"\${array[-'{1..${#array[@]}}']}\"'".
ind=("${!array[@]}");eval eval echo "'\"\${array[ind[-'{1..${#array[@]}}']]}\"'"
Aunque no voy a decir algo nuevo y también lo usaré tacpara revertir la matriz, creo que valdría la pena mencionar la siguiente solución de línea única con la versión bash 4.4:
$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}" |tac)
Pruebas:
$ 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
Tenga en cuenta que el nombre de var dentro de read es el nombre de la matriz original, por lo que no se requiere una matriz auxiliar para el almacenamiento temporal.
Implementación alternativa ajustando 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")
PD: Creo que las soluciones anteriores no funcionarán en la siguiente bashversión 4.4debido a la diferente readimplementación de la función bash builtin.
IFSversión funciona sino que también está imprimiendo: declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="10" [7]="11" [8]="12"). Usando bash 4.4-5. Debes eliminar ;declare -p arrayal final de la primera línea, luego funciona ...
declare -pes solo una forma rápida de hacer que bash imprima la matriz real (índice y contenido). No necesita este declare -pcomando en su script real. Si algo sale mal en las asignaciones de sus matrices, podría terminar en un caso que ${array[0]}="1 2 3 4 5 6 10 11 12"= todos los valores almacenados en el mismo índice; usando echo no verá ninguna diferencia. Para una impresión de matriz rápida, usará declare -p arrayle devolverá las indeces de matriz real y el valor correspondiente en cada índice.
read -d'\n'método no funcionó para ti?
read -d'\n'funciona bien.
Para invertir una matriz arbitraria (que puede contener cualquier número de elementos con cualquier valor):
Con zsh:
array_reversed=("${(@Oa)array}")
Con bash4.4+, dado que las bashvariables no pueden contener bytes NUL de todos modos, puede usar GNU tac -s ''en los elementos impresos como registros delimitados por NUL:
readarray -td '' array_reversed < <(
((${#array[@]})) && printf '%s\0' "${array[@]}" | tac -s '')
POSIXly, para revertir la matriz de shell POSIX ( $@, hecha de $1, $2...):
code='set --'
n=$#
while [ "$n" -gt 0 ]; do
code="$code \"\${$n}\""
n=$((n - 1))
done
eval "$code"
Solución de golpe puro, funcionaría como una sola línea.
$: 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]}" )parece más simple.
también puedes considerar usar seq
array=(1 2 3 4 5 6 7)
for i in $(seq $((${#array[@]} - 1)) -1 0); do
echo ${array[$i]}
done
en freebsd puede omitir -1 parámetro de incremento:
for i in $(seq $((${#array[@]} - 1)) 0); do
echo ${array[$i]}
done
array=(1 2 3 4 5 6 7)
echo "${array[@]} " | tac -s ' '
O
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
tacya se ha mencionado: unix.stackexchange.com/a/412874/260978 , unix.stackexchange.com/a/467924/260978 , unix.stackexchange.com/a/413176/260978