Відповіді:
Я відповів на запитання як написаний, і цей код повертає масив. (Друк елементів у зворотному порядку без повернення масиву - це лише for
цикл, відлік від останнього елемента до нуля.) Це стандартний алгоритм "підміняти перший і останній".
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[@]}"
Він працює для масивів непарної і парної довжини.
Ще один нетрадиційний підхід:
#!/bin/bash
array=(1 2 3 4 5 6 7)
f() { array=("${BASH_ARGV[@]}"); }
shopt -s extdebug
f "${array[@]}"
shopt -u extdebug
echo "${array[@]}"
Вихід:
7 6 5 4 3 2 1
Якщо extdebug
це ввімкнено, масив BASH_ARGV
містить у функції всі позиційні параметри у зворотному порядку.
Нетрадиційний підхід (все не чисто bash
):
якщо всі елементи масиву є лише одним символом (як у запитанні), ви можете використовувати rev
:
echo "${array[@]}" | rev
інакше:
printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
і якщо ви можете використовувати zsh
:
echo ${(Oa)array}
tac
, як протилежність cat
досить добре запам’ятати, ДЯКУЮ!
rev
, мені потрібно зазначити, що rev
вона не працюватиме правильно для чисел з двозначними цифрами. Наприклад, елемент масиву з 12
використанням rev буде надрукований як 21
. Спробуйте ;-)
Якщо ви насправді хочете реверс в іншому масиві:
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
}
Потім:
array=(1 2 3 4)
reverse array foo
echo "${foo[@]}"
Дає:
4 3 2 1
Це має правильно обробляти випадки, коли індекс масиву відсутній, скажімо, у вас був array=([1]=1 [2]=2 [4]=4)
, і в цьому випадку циклічне від 0 до найвищого індексу може додавати додаткові, порожні, елементи.
shellcheck
друкує два попередження: array=(1 2 3 4)
<-- SC2034: array appears unused. Verify it or export it.
і:echo "${foo[@]}"
<-- SC2154: foo is referenced but not assigned.
declare
лінія.
declare -n
здається, не працює у баш-версіях до 4.3.
Щоб поміняти місцями масиви на місцях (навіть із розрідженими масивами) (починаючи з 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[@]}"
На виконання:
./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
Для старшого bash вам потрібно використовувати цикл (у bash (з 2.04)) та використовуючи, $a
щоб уникнути пробілу:
#!/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
Для баш з 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
Також (за допомогою оператора заборгованого побітного відхилення) (починаючи з 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
Некрасивий, незрозумілий, але однолінійний:
eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"
eval eval echo "'\"\${array[-'{1..${#array[@]}}']}\"'"
.
ind=("${!array[@]}");eval eval echo "'\"\${array[ind[-'{1..${#array[@]}}']]}\"'"
Хоча я не збираюсь розповідати щось нове, і я також буду використовувати tac
для зворотного перегляду масиву, я хотів би сказати, що це було б згадати нижче одне рядкове рішення з використанням bash версії 4.4:
$ read -d'\n' -a array < <(printf '%s\n' "${array[@]}" |tac)
Тестування:
$ 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
Зверніть увагу, що ім'я var усередині read - це ім'я як оригінальний масив, тому для зберігання temp не потрібен допоміжний масив.
Альтернативна реалізація шляхом коригування 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: Я думаю, що вищезгадані рішення не працюватимуть у bash
нижченаведеній версії 4.4
через різну read
реалізацію вбудованої функції bash.
IFS
Версія працює , але це також друк: declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="10" [7]="11" [8]="12")
. Використання bash 4.4-5
. Ви повинні видалити ;declare -p array
в кінці першого рядка, то він працює ...
declare -p
- це лише швидкий спосіб зробити баш-друк реального масиву (індекс та вміст). Ця declare -p
команда не потрібна у вашому реальному сценарії. Якщо у ваших призначеннях масивів щось піде не так, ви можете виявити випадок, що ${array[0]}="1 2 3 4 5 6 10 11 12"
= всі значення, що зберігаються в одному індексі - за допомогою echo ви не побачите різниці. Для швидкого роздруковування масиву використання declare -p array
поверне вам справжні масиви та відповідне значення у кожному індексі.
read -d'\n'
метод для вас не працював?
read -d'\n'
працює чудово.
Щоб повернути довільний масив (який може містити будь-яку кількість елементів з будь-якими значеннями):
З zsh
:
array_reversed=("${(@Oa)array}")
З bash
4.4+, враховуючи, що bash
змінні ніяк не можуть містити балів NUL, ви можете використовувати GNU tac -s ''
для елементів, надрукованих як записи з обмеженою NUL:
readarray -td '' array_reversed < <(
((${#array[@]})) && printf '%s\0' "${array[@]}" | tac -s '')
POSIXly, щоб повернути масив оболонки POSIX ( $@
зроблений з $1
, $2
...):
code='set --'
n=$#
while [ "$n" -gt 0 ]; do
code="$code \"\${$n}\""
n=$((n - 1))
done
eval "$code"
Чистий розчин bash, працював би як однолінійний.
$: 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]}" )
здається простіше.
Ви також можете розглянути можливість використання seq
array=(1 2 3 4 5 6 7)
for i in $(seq $((${#array[@]} - 1)) -1 0); do
echo ${array[$i]}
done
у freebsd ви можете опустити параметр збільшення -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 ' '
Або
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