Bash'de bir dizi nasıl kesilir


197

Bash (1) man sayfasındaki "Array" bölümüne baktığımda, bir diziyi dilimlemenin bir yolunu bulamadım.

Bu yüzden bu aşırı karmaşık işleve geldim:

#!/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 )"
}

Bunun gibi kullanılır:

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

Bunu yapmanın daha iyi bir yolu var mı?


Ben bir dizinin sonunda dilim nasıl arıyordu ve burada yönlendirildi. Cevap burada bulunmuyor ve bunun bir kopyası olacak, çünkü cevabı burada buldum stackoverflow.com/questions/44939747/… . Temel fikir $ {# array [@]} - (2 + 7) gibi $ {array: offset: length} yapısında uzunluğun beklendiği aritmetik bir ifadeye sahip olabilmemizdir. Burada verilen cevapların hiçbiri bunu göstermemektedir.
Dominic108

Yanıtlar:


313

Bkz Parametre Genişleme Bash bölümüne mansayfası. A[@]dizinin içeriğini döndürür, :1:2dizin 1'den başlayarak uzunluk 2 bir dilim alır.

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" nin bir dizi öğesi (ve fazladan bir boşluk içerdiği) gerçeğinin korunduğuna dikkat edin.


2
Güzel. Dizi bölümüne baktım ve orada görmedim.
Chen Levy

36
Bu aptal Chen, neden Dizi bölümünde olsun ki? * sarc
deltaray

1
@AquariusPower: endeksleri dizisi yaratmak ve dilim: idx=(${!A[@]}); echo ${idx[@]:1}.
sonraki duyuruya kadar duraklatıldı.

7
@Feuermurmel: Sadece indeksleme köşeli parantez olmadan yapın:${@:1:2}
sonraki duyuruya kadar duraklatıldı.

5
@DennisWilliamson Ben dönüştürmek için gerekli bulundu $@: boşluklar bölünme alacağı bulunan bu ya argümanları yapmadan önce uygun bir diziyeARGS=( "$@" ); ARGS_AFTER_FIRST=( "${ARGS[@]:1}" )
Heath Sınırlar

47

Dizinin tüm öğelerini belirtilen dizinden başlayarak almak için uygun bir kısayol da vardır. Örneğin "$ {A [@]: 1}", dizinin "kuyruğu", yani ilk öğesi olmayan dizidir.

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

8
Ve siz oradayken:echo "${A[@]::1}" # 4
Chen Levy

7
Bu harika, ancak bir işlev içinde kullanılırsa, okumak için biraz değiştirilmesi gerektiğine dikkat edilmelidir "${${@}[@]:1}".
Alex Gray

@AlexGray: Bu bana "kötü ikame" veriyor, ama ${@:2}iyi çalışıyor.
Nick Matteo

3

Python'daki gibi dizi dilimleme ( Rebash kütüphanesinden):

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

Diyelim ki ben kullanıcıdan bir dizi okuyorum, o zaman ben herşey dahil 3 ila 7 eleman görmek istiyorum.

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


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

4
Kodunuzdaki dilim sözdizimi, 8 yaşında kabul edilen yanıttaki ile aynıdır. Cevabınız yeni bir şey eklemiyor.
melpomene
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.