web-dev-qa-db-ja.com

Bash-配列を逆にする

配列を逆にする簡単な方法はありますか?

#!/bin/bash

array=(1 2 3 4 5 6 7)

echo "${array[@]}"

だから私は得るでしょう:7 6 5 4 3 2 1
の代わりに: 1 2 3 4 5 6 7

18
nath

私は書かれたとおりに質問に答えました、そしてこのコードは配列を逆にします。 (配列を逆にせずに逆の順序で要素を出力するのは、最後の要素からゼロまでカウントダウンする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[@]}"

奇数長と偶数長の配列で機能します。

15
roaima

別の型破りなアプローチ:

#!/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には、すべての定位置パラメーターが逆の順序で関数に含まれています。

18
Cyrus

型破りなアプローチ(純粋なbashではない):

  • 配列のすべての要素が(質問のように)1文字だけの場合は、revを使用できます。

    echo "${array[@]}" | rev
    
  • さもないと:

    printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
    
  • zshを使用できる場合:

    echo ${(Oa)array}
    
16
jimmij

(疎な配列であっても)配列の位置を入れ替えるには(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以降のbashの場合:

#!/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
8
Isaac

別の配列で実際に逆にしたい場合:

_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から最高のインデックスまでループすることで、追加の空の要素が追加されることがあります。

8
muru

醜い、メンテナンス不可能な、ワンライナー:

eval eval echo "'\"\${array['{$((${#array[@]}-1))..0}']}\"'"
3
user23013

純粋なbashソリューションは、ワンライナーとして機能します。

$: for (( i=${#array[@]}-1; i>=0; i-- ))
>  do rev[${#rev[@]}]=${array[i]}
>  done
$: echo  "${rev[@]}"
7 6 5 4 3 2 1
2
Paul Hodges

任意の配列(任意の値を持つ任意の数の要素を含むことができる)を逆にするには:

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"
1

新しいことを言うつもりはありませんが、配列を逆にするために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

Read内のvar名は元の配列としての名前であるため、一時ストレージにヘルパー配列は必要ありません。

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.4read bash組み込み関数の実装が異なるため。

1
George Vasiliou