Bashで配列をスライスする方法


194

bash(1)のmanページの「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 [@]}-(2 + 7)などの算術式を使用できるということです。ここで、長さは$ {array:offset:length}で期待されます。ここで提供されている回答のいずれもそれを示していません。
Dominic108

回答:


310

Bash ページのパラメーター拡張セクションを参照して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」は1つの配列要素である(そして余分なスペースが含まれている)ことは維持されることに注意してください。


2
涼しい。配列セクションを調べたところ、そこにはありませんでした。
陳レビー

36
それはばかげた陳です、なぜそれが配列セクションにあるのでしょうか?* sarc
deltaray、

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 Grey

@AlexGray:ここでは「悪い置換」ができますが、${@:2}正常に動作します。
Nick Matteo

3

Pythonのような配列スライス(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年前に承認された回答の構文と同じです。あなたの答えは何も新しいものを追加しません。
メルポメン2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.