Looking the "Array" section in the bash(1) man page, I didn't find a way to slice an array.
在bash(1)手册页中查看“数组”一节,我没有找到分割数组的方法。
So I came up with this overly complicated 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 -i i
local tmp=$(for i in $indexes
do echo "$(eval echo \"\${$input[$i]}\")"
done)
local IFS=$'\n'
eval $output="( \$tmp )"
}
Used like this:
使用这样的:
$ A=( foo bar "a b c" 42 )
$ slice B A 1 2
$ echo "${B[0]}" # bar
$ echo "${B[1]}" # a b c
Is there a better way to do this?
有更好的方法吗?
4 个解决方案
#1
200
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.
参见Bash man页面中的参数展开部分。A[@]返回数组的内容,:1:2取长度为2的切片,从索引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
Note that the fact that "a b c" is one array element (and that it contains an extra space) is preserved.
注意,“a b c”是一个数组元素(并且它包含一个额外的空间)这一事实被保留了下来。
#2
42
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.
还有一个方便的快捷方式,可以从指定的索引开始获取数组的所有元素。例如,“${A[@]:1}”将是数组的“尾部”,即没有第一个元素的数组。
version=4.7.1
A=( ${version//\./ } )
echo "${A[@]}" # 4 7 1
B=( "${A[@]:1}" )
echo "${B[@]}" # 7 1
#3
1
Array slicing like in Python (From the rebash library):
如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"
#4
0
Lets say I am reading an array from user, then I want to see element 3 to 7 both inclusive .
假设我正在从user读取一个数组,然后我想看到元素3到7都包含在内。
cnt=0
while read var;
do
myarr[cnt]=$var
cnt=$((cnt+1))
done
echo ${myarr[@]:3:5}
#1
200
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.
参见Bash man页面中的参数展开部分。A[@]返回数组的内容,:1:2取长度为2的切片,从索引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
Note that the fact that "a b c" is one array element (and that it contains an extra space) is preserved.
注意,“a b c”是一个数组元素(并且它包含一个额外的空间)这一事实被保留了下来。
#2
42
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.
还有一个方便的快捷方式,可以从指定的索引开始获取数组的所有元素。例如,“${A[@]:1}”将是数组的“尾部”,即没有第一个元素的数组。
version=4.7.1
A=( ${version//\./ } )
echo "${A[@]}" # 4 7 1
B=( "${A[@]:1}" )
echo "${B[@]}" # 7 1
#3
1
Array slicing like in Python (From the rebash library):
如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"
#4
0
Lets say I am reading an array from user, then I want to see element 3 to 7 both inclusive .
假设我正在从user读取一个数组,然后我想看到元素3到7都包含在内。
cnt=0
while read var;
do
myarr[cnt]=$var
cnt=$((cnt+1))
done
echo ${myarr[@]:3:5}