How to slice an array in Bash How to slice an array in Bash bash bash

How to slice an array in Bash


See the Parameter Expansion section in the Bash man page. A[@] returns the contents of the array, :1:2 takes a slice of length 2, starting at index 1.

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

Note that the fact that a b c is one array element (and that it contains an extra space) is preserved.


There is also a convenient shortcut to get all elements of the array starting with specified index. For example "${A[@]:1}" would be the "tail" of the array, that is the array without its first element.

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


Array slicing like in Python (From the rebash library):

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"