Cara mengiris array di Bash

194

Melihat bagian "Array" di halaman manual bash (1), saya tidak menemukan cara untuk mengiris array.

Jadi saya datang dengan fungsi yang terlalu rumit ini:

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

Digunakan seperti ini:

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

Apakah ada cara yang lebih baik untuk melakukan ini?

Chen Levy
sumber
Saya sedang mencari cara memotong ujung array dan diarahkan ke sini. Jawabannya tidak ditemukan di sini dan itu akan menjadi duplikat untuk melakukannya, karena saya menemukan jawabannya di sini stackoverflow.com/questions/44939747/… . Ide dasarnya adalah kita dapat memiliki ekspresi aritmatika seperti $ {# array [@]} - (2 + 7) di mana panjangnya diharapkan dalam konstruk $ {array: offset: length}. Tidak ada jawaban yang diberikan di sini yang menggambarkan hal itu.
Dominic108

Jawaban:

310

Lihat bagian Ekspansi Parameter di manhalaman Bash . A[@]mengembalikan konten array, :1:2membutuhkan sepotong panjang 2, mulai dari indeks 1.

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

Perhatikan bahwa fakta bahwa "ab c" adalah satu elemen array (dan itu berisi ruang tambahan) dipertahankan.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
2
Keren. Saya melihat di bagian Array, dan tidak melihatnya di sana.
Chen Levy
36
Itu konyol Chen, mengapa itu ada di bagian Array? * sarc
deltaray
1
@AquariusPower: Buat array indeks dan iris: idx=(${!A[@]}); echo ${idx[@]:1}.
Dijeda sampai pemberitahuan lebih lanjut.
7
@Feuermurmel: Lakukan saja tanpa kurung pengindeksan:${@:1:2}
Dijeda sampai pemberitahuan lebih lanjut.
5
@ DennisWilliamson Saya menemukan bahwa saya perlu mengkonversi $@ke array yang tepat sebelum melakukan ini atau argumen yang berisi spasi akan terpecah:ARGS=( "$@" ); ARGS_AFTER_FIRST=( "${ARGS[@]:1}" )
Heath Borders
47

Ada juga jalan pintas yang nyaman untuk mendapatkan semua elemen array dimulai dengan indeks yang ditentukan. Misalnya "$ {A [@]: 1}" akan menjadi "ekor" dari array, yaitu array tanpa elemen pertama.

version=4.7.1
A=( ${version//\./ } )
echo "${A[@]}"    # 4 7 1
B=( "${A[@]:1}" )
echo "${B[@]}"    # 7 1
Nicholas Sushkin
sumber
8
Dan sementara Anda melakukannya:echo "${A[@]::1}" # 4
Chen Levy
7
Ini bagus, tetapi harus dicatat bahwa jika digunakan dalam suatu fungsi, itu harus diubah sedikit untuk dibaca "${${@}[@]:1}".
Alex Grey
@AlexGray: Itu memberi saya "substitusi buruk" di sini, tetapi ${@:2}berfungsi dengan baik.
Nick Matteo
3

Array mengiris seperti di Python (Dari perpustakaan 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"
jandob
sumber
1

Katakanlah saya membaca array dari pengguna, maka saya ingin melihat elemen 3 hingga 7 termasuk keduanya.

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


echo ${myarr[@]:3:5}
Arindam Roychowdhury
sumber
4
Sintaks irisan dalam kode Anda identik dengan yang ada dalam jawaban yang diterima 8 tahun. Jawaban Anda menambahkan tidak ada yang baru.
melpomene