Bash에서 배열을 슬라이스하는 방법


197

bash (1) 매뉴얼 페이지의 "Array"섹션을 보면 배열을 슬라이스하는 방법을 찾지 못했습니다.

그래서 나는이 지나치게 복잡한 기능을 생각해 냈습니다.

#!/bin/bash

# @brief: slice a bash array
# @arg1:  output-name
# @arg2:  input-name
# @args:  seq args
# ----------------------------------------------
function slice() {
   local output=$1
   local input=$2
   shift 2
   local indexes=$(seq $*)

   local -i i
   local tmp=$(for i in $indexes 
                 do echo "$(eval echo \"\${$input[$i]}\")" 
               done)

   local IFS=$'\n'
   eval $output="( \$tmp )"
}

이런 식으로 사용 :

$ A=( foo bar "a  b c" 42 )
$ slice B A 1 2
$ echo "${B[0]}"  # bar
$ echo "${B[1]}"  # a  b c

더 좋은 방법이 있습니까?


배열의 끝을 잘라내는 방법을 찾고 있었고 여기로 지시되었습니다. 대답은 여기에 없으며 여기에서 대답을 찾았 기 때문에 중복됩니다 . stackoverflow.com/questions/44939747/… 기본 아이디어는 $ {array : offset : length} 구문에서 길이가 예상되는 $ {# array [@]}-(2 + 7)과 같은 산술 표현식을 가질 수 있다는 것입니다. 여기에 제공된 답변 중 어느 것도 그것을 설명하지 않습니다.
Dominic108

답변:


313

Bash 페이지 의 Parameter Expansion 섹션을 man참조하십시오. A[@]배열의 내용을 반환하고 :1:2인덱스 1부터 시작하여 길이 2의 슬라이스를 가져옵니다.

A=( foo bar "a  b c" 42 )
B=("${A[@]:1:2}")
C=("${A[@]:1}")       # slice to the end of the array
echo "${B[@]}"        # bar a  b c
echo "${B[1]}"        # a  b c
echo "${C[@]}"        # bar a  b c 42
echo "${C[@]: -2:2}"  # a  b c 42 # The space before the - is necesssary

"ab c"가 하나의 배열 요소이며 추가 공간을 포함한다는 사실이 유지됩니다.


2
멋있는. 배열 섹션을 보았는데 거기에서 보지 못했습니다.
Chen Levy

36
어리석은 첸인데 왜 배열 섹션에 있을까요? * sarc
델타 레이

1
@AquariusPower : 인덱스 배열을 만들고 슬라이스합니다 : idx=(${!A[@]}); echo ${idx[@]:1}.
추후 공지가있을 때까지 일시 중지되었습니다.

7
@Feuermurmel : 색인 대괄호없이 바로 수행하십시오.${@:1:2}
추후 공지가있을 때까지 일시 중지되었습니다.

5
@DennisWilliamson 나는 $@이것을하기 전에 적절한 배열 로 변환 해야하거나 공백이 포함 된 인수가 분리 될 것이라는 것을 알았습니다 .ARGS=( "$@" ); ARGS_AFTER_FIRST=( "${ARGS[@]:1}" )
Heath Borders

47

지정된 인덱스로 시작하는 배열의 모든 요소를 ​​가져 오는 편리한 바로 가기도 있습니다. 예를 들어 "$ {A [@] : 1}"은 배열의 "꼬리", 즉 첫 번째 요소가없는 배열입니다.

version=4.7.1
A=( ${version//\./ } )
echo "${A[@]}"    # 4 7 1
B=( "${A[@]:1}" )
echo "${B[@]}"    # 7 1

8
그리고 당신이 그것에있는 동안 :echo "${A[@]::1}" # 4
Chen Levy

7
이것은 훌륭하지만 함수 내에서 사용되는 경우 읽기 위해 약간 변경되어야합니다 "${${@}[@]:1}".
Alex Gray

@AlexGray : 여기서 "나쁜 대체"를 제공하지만 ${@:2}잘 작동합니다.
Nick Matteo

3

파이썬에서와 같이 배열 슬라이싱 ( rebash 라이브러리에서) :

array_slice() {
    local __doc__='
    Returns a slice of an array (similar to Python).

    From the Python documentation:
    One way to remember how slices work is to think of the indices as pointing
    between elements, with the left edge of the first character numbered 0.
    Then the right edge of the last element of an array of length n has
    index n, for example:
    ```
    +---+---+---+---+---+---+
    | 0 | 1 | 2 | 3 | 4 | 5 |
    +---+---+---+---+---+---+
    0   1   2   3   4   5   6
    -6  -5  -4  -3  -2  -1
    ```

    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 1:-2 "${a[@]}")
    1 2 3
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 0:1 "${a[@]}")
    0
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice 1:1 "${a[@]}")" ] && echo empty
    empty
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice 2:1 "${a[@]}")" ] && echo empty
    empty
    >>> local a=(0 1 2 3 4 5)
    >>> [ -z "$(array.slice -2:-3 "${a[@]}")" ] && echo empty
    empty
    >>> [ -z "$(array.slice -2:-2 "${a[@]}")" ] && echo empty
    empty

    Slice indices have useful defaults; an omitted first index defaults to
    zero, an omitted second index defaults to the size of the string being
    sliced.
    >>> local a=(0 1 2 3 4 5)
    >>> # from the beginning to position 2 (excluded)
    >>> echo $(array.slice 0:2 "${a[@]}")
    >>> echo $(array.slice :2 "${a[@]}")
    0 1
    0 1

    >>> local a=(0 1 2 3 4 5)
    >>> # from position 3 (included) to the end
    >>> echo $(array.slice 3:"${#a[@]}" "${a[@]}")
    >>> echo $(array.slice 3: "${a[@]}")
    3 4 5
    3 4 5

    >>> local a=(0 1 2 3 4 5)
    >>> # from the second-last (included) to the end
    >>> echo $(array.slice -2:"${#a[@]}" "${a[@]}")
    >>> echo $(array.slice -2: "${a[@]}")
    4 5
    4 5

    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -4:-2 "${a[@]}")
    2 3

    If no range is given, it works like normal array indices.
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -1 "${a[@]}")
    5
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice -2 "${a[@]}")
    4
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 0 "${a[@]}")
    0
    >>> local a=(0 1 2 3 4 5)
    >>> echo $(array.slice 1 "${a[@]}")
    1
    >>> local a=(0 1 2 3 4 5)
    >>> array.slice 6 "${a[@]}"; echo $?
    1
    >>> local a=(0 1 2 3 4 5)
    >>> array.slice -7 "${a[@]}"; echo $?
    1
    '
    local start end array_length length
    if [[ $1 == *:* ]]; then
        IFS=":"; read -r start end <<<"$1"
        shift
        array_length="$#"
        # defaults
        [ -z "$end" ] && end=$array_length
        [ -z "$start" ] && start=0
        (( start < 0 )) && let "start=(( array_length + start ))"
        (( end < 0 )) && let "end=(( array_length + end ))"
    else
        start="$1"
        shift
        array_length="$#"
        (( start < 0 )) && let "start=(( array_length + start ))"
        let "end=(( start + 1 ))"
    fi
    let "length=(( end - start ))"
    (( start < 0 )) && return 1
    # check bounds
    (( length < 0 )) && return 1
    (( start < 0 )) && return 1
    (( start >= array_length )) && return 1
    # parameters start with $1, so add 1 to $start
    let "start=(( start + 1 ))"
    echo "${@: $start:$length}"
}
alias array.slice="array_slice"

1

사용자로부터 배열을 읽은 다음 요소 3 ~ 7을 모두보고 싶습니다.

cnt=0
while read var;
    do
    myarr[cnt]=$var
    cnt=$((cnt+1)) 
    done


echo ${myarr[@]:3:5}

4
코드의 슬라이스 구문은 8 년 동안 수락 된 답변의 구문과 동일합니다. 당신의 대답은 새로운 것을 추가하지 않습니다.
melpomene
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.