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 for
ciclo 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 extdebug
está habilitado, la matriz BASH_ARGV
contiene 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[@]}" | rev
de otra manera:
printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
y si puedes usar zsh
:
echo ${(Oa)array}
tac
, como lo opuesto a cat
bastante bueno para recordar, ¡GRACIAS!
rev
, necesito mencionar que rev
no 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.
shellcheck
imprime 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.
declare
línea.
declare -n
parece 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 $a
para 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é tac
para 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 bash
versión 4.4
debido a la diferente read
implementación de la función bash builtin.
IFS
versió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 array
al final de la primera línea, luego funciona ...
declare -p
es solo una forma rápida de hacer que bash imprima la matriz real (índice y contenido). No necesita este declare -p
comando 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 array
le 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 bash
4.4+, dado que las bash
variables 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
tac
ya se ha mencionado: unix.stackexchange.com/a/412874/260978 , unix.stackexchange.com/a/467924/260978 , unix.stackexchange.com/a/413176/260978