如何在bash中切片数组

(编辑以适应答案)

看bash(1)手册页中的“数组”部分,我没有find切片bash数组的方法。

所以我想出了这个过于复杂的function:

#!/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 -ii local tmp=$(for i in $indexes do echo "$(eval echo \"\${$input[$i]}\")" done) local IFS=$'\n' eval $output="( \$tmp )" } 

像这样使用:

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

有没有更好的方法来做到这一点?

请参阅Bash man页中的参数扩展部分。 A[@]返回数组的内容, :1:2从索引1开始,获取长度为2的片段。

 A=( foo bar "abc" 42 ) B=("${A[@]:1:2}") C=("${A[@]:1}") # slice to the end of the array echo "${B[@]}" # bar abc echo "${B[1]}" # abc echo "${C[@]}" # bar abc 42 

请注意,“abc”是一个数组元素(并且它包含一个额外的空间)的事实被保留。

还有一个方便的快捷方式来获取以指定索引开始的数组的所有元素。 例如“$ {A [@]:1}”就是数组的“尾部”,也就是没有第一个元素的数组。

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

像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" 

比方说,我正在阅读从用户数组,然后我想看到元素3至7,包括两个端点。

 cnt=0 while read var; do myarr[cnt]=$var cnt=$((cnt+1)) done echo ${myarr[@]:3:5}