배쉬-배열을 반대로


16

배열을 뒤집는 간단한 방법이 있습니까?

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

답변:


15

작성된 질문에 대답 했으며이 코드는 배열을 반대로 바꿉니다. (배열을 반전시키지 않고 요소를 역순으로 인쇄하는 for것은 마지막 요소에서 0으로 카운트 다운 하는 루프 일뿐 입니다.) 이것은 표준 "첫 번째와 마지막 스왑"알고리즘입니다.

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[@]}"

홀수 및 짝수 길이의 배열에서 작동합니다.


희소 배열에는이 기능이 작동하지 않습니다.
Isaac

@Isaac 처리 해야하는 경우 StackOverflow에 대한 솔루션이 있습니다.
roaima


18

또 다른 비 전통적인 접근 방식 :

#!/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역순 모든 위치 매개 변수 함수로 포함한다.


이것은 멋진 트릭입니다!
Valentin Bajrami

15

비 전통적인 접근 방식 (모두 순수하지 않음 bash) :

  • 배열의 모든 요소가 하나의 문자 (질문과 같이) 인 경우 다음을 사용할 수 있습니다 rev.

    echo "${array[@]}" | rev
  • 그렇지 않으면:

    printf '%s\n' "${array[@]}" | tac | tr '\n' ' '; echo
  • 그리고 당신이 사용할 수 있다면 zsh:

    echo ${(Oa)array}

그냥 찾고 tac의 반대로서 cat, 감사를 기억하는 아주 좋은!
nath December

3
의 아이디어가 마음에 들지만 두 자리 숫자의 경우 제대로 작동하지 않는다는 rev것을 언급해야합니다 rev. 예를 들어 12 rev 를 사용 하는 배열 요소 는로 인쇄됩니다 21. 그것을 시도하십시오 ;-)
George Vasiliou

@GeorgeVasiliou 예, 모든 요소가 하나의 문자 (숫자, 문자, 문장 부호 등) 인 경우에만 작동합니다. 그래서 두 번째로 더 일반적인 솔루션을 제공했습니다.
jimmij

8

실제로 다른 배열에서 반전을 원할 경우 :

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.
nath

1
@nath 그들은 간접적으로 사용됩니다 declare.
muru

영리하지만 declare -n4.3 이전의 bash 버전에서는 작동하지 않는 것 같습니다.
G-Man, 'Reinstate

8

배열 위치를 적절한 위치로 교체하려면 (bash 배열 이후에도) (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

네거티브 첨자로 배열의 요소를 거꾸로 지정하면 4.3 이전의 bash 버전에서는 작동하지 않는 것 같습니다.
G-Man, 'Reinstate

1
실제로 4.2- 알파에서 음수 주소 지정이 변경되었습니다. 그리고 부정 된 값을 가진 스크립트는 해당 버전에서 작동합니다. @ G-Man p. 인덱스 배열에 대한 음의 아래 첨자, 이제 최대 할당 된 인덱스 + 1의 오프셋으로 처리 되지만 Bash-hackers는 부정 인덱스를 사용하여 끝에서
Isaac

3

미운, 유지 불가능하지만 하나의 라이너 :

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

더 간단하지는 않지만 더 짧습니다 eval eval echo "'\"\${array[-'{1..${#array[@]}}']}\"'".
Isaac Isaac

그리고 희소 배열도 :ind=("${!array[@]}");eval eval echo "'\"\${array[ind[-'{1..${#array[@]}}']]}\"'"
Isaac

@Isaac 그러나 불행히도 더 이상 하나의 라이너가 아니며 드문 배열 버전에 대해서는 추악하고 유지 보수 할 수 없습니다. (단, 소형 어레이의 파이프보다 여전히 빠를 수 있습니다.)
user23013

기술적으로는 "한 줄짜리"입니다. 하나의 명령이 아니라 "하나의 라이너"입니다. 예, 매우 추악하고 유지 관리 문제이지만 동의하는 것은 재미 있습니다.
Isaac Isaac

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

읽은 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")

추신 : 위의 솔루션은 bash 내장 함수 구현이 다르기 때문에 bash아래 버전 에서 작동하지 않는다고 생각 합니다.4.4read


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첫 번째 줄의 끝에서 제거 해야합니다. 그러면 작동합니다.
nath

1
@nath declare -p는 bash가 실제 배열 (인덱스 및 내용)을 인쇄하는 빠른 방법입니다. declare -p실제 스크립트에는 이 명령 이 필요하지 않습니다 . 배열 할당에서 문제가 발생하면 ${array[0]}="1 2 3 4 5 6 10 11 12"= = 모든 값이 동일한 인덱스에 저장된 경우 echo를 사용하면 차이가 없습니다. 빠른 배열 인쇄물을 사용 declare -p array하면 실제 색인 및 각 색인의 해당 값이 반환됩니다.
George Vasiliou

@nath 그런데, 그 read -d'\n'방법은 당신을 위해 작동하지 않았다?
George Vasiliou

read -d'\n'잘 작동합니다.
nath December

아아! SORRY :-)
nath December

1

임의의 배열을 뒤집으려면 (값을 가진 여러 요소를 포함 할 수 있음) :

zsh:

array_reversed=("${(@Oa)array}")

bash4.4, 주어진 bash변수는 GNU를 사용하여, NUL 어쨌든 바이트를 포함 할 수 없습니다 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

순수한 bash 솔루션은 하나의 라이너로 작동합니다.

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

좋은 것 !!! 고마워; 여기에 복사 할 하나의 라이너 :-)`array = (1 2 3 4 5 6 7); for ((i = $ {# array [@]}-1; i> = 0; i--)); do rev [$ {# rev [@]}] = $ {array [i]}; 끝난; echo "$ {rev [@]}"`
nath

하는 rev+=( "${array[i]}" )것이 더 간단 해 보입니다.
Isaac

하나의 여섯, 다른 하나는 여섯입니다. 나는 그 구문을 강구하지는 않지만 그 이유가 없다-단지 편견과 선호. 넌 너대로 해.
Paul Hodges

-1

당신은 또한 사용을 고려할 수 있습니다 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

이것은 배열을 뒤집지 않고 단지 역순으로 인쇄합니다.
roaima

동의, 내 요점은 대안으로 인덱스 액세스를 고려하는 것이 었습니다.
M. Modugno

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.