如何在中间的某个索引处移动 bash 数组?

如何在中间的某个索引处移动 bash 数组?
1  #!/bin/bash
2  # query2.sh
3
4  numbers=(53 8 12 9 784 69 8 7 1)
5  i=4
6
7  echo ${numbers[@]} # <--- this echoes "53 8 12 9 784 69 8 7 1" to stdout.
8  echo ${numbers[i]} # <--- this echoes "784" to stdout.
9
10 unset numbers[i]
11
12 echo ${numbers[@]} # <--- this echoes "53 8 12 9 69 8 7 1" to stdout.
13 echo ${numbers[i]} # <--- stdout is blank.

考虑到根据第 12 行的 stdout 判断数组似乎已更新,为什么第 13 行中的 stdout 为空?

因此,我应该怎样做才能得到预期的答案“69”?

答案1

unset删除一个元素。它不会对剩余元素重新编号。

我们可以用它declare -p来看看到底发生了什么numbers

$ unset "numbers[i]"
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

观察numbers不再有元素4

另一个例子

观察:

$ a=()
$ a[1]="element 1"
$ a[22]="element 22"
$ declare -p a
declare -a a=([1]="element 1" [22]="element 22")

数组a没有元素 2 到 21。Bash 不要求数组索引是连续的。

强制对索引重新编号的建议方法

让我们从numbers缺少元素的数组开始4

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

如果我们希望改变索引,那么:

$ numbers=("${numbers[@]}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

现在有一个元素编号4并且它具有值69

一步删除元素并对数组重新编号的另一种方法

再次,让我们定义numbers

$ numbers=(53 8 12 9 784 69 8 7 1)

正如建议的托比·斯佩特在注释中,有一种方法可以一步删除第五个元素(位于索引 4 处)并对剩余元素重新编号:

$ numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

正如您所看到的,第五个元素被删除,所有剩余元素都被重新编号。

${numbers[@]:0:4}slices 数组numbers:它获取从元素 0 开始的前四个元素。

类似地,${numbers[@]:5}切片数组numbers:它获取从元素 5 开始一直到数组末尾的所有元素。

获取数组的索引

价值观可以使用 获得数组的${a[@]}.为了找到指数(或者) 对应于这些值,使用${!a[@]}.

例如,再次考虑我们的数组numbers缺少元素4

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

要查看分配了哪些索引:

$ echo "${!numbers[@]}"
0 1 2 3 5 6 7 8

同样,4索引列表中缺少该值。

文档

man bash

内置unset函数用于销毁数组。 unset name[subscript]销毁索引处的数组元素subscript。索引数组的负下标的解释如上所述。必须小心避免路径名扩展引起的不良副作用。 unset name,其中name是数组,或者unset name[subscript],其中subscript* 或者 @,删除整个数组。

答案2

bash像 in 那样的数组ksh并不是真正的数组,它们更像是键限于正整数的关联数组(或所谓的稀疏数组)。对于具有真实数组的 shell,您可以查看诸如rcesfishyash、之类的 shell zsh(甚至csh/tcsh尽管这些 shell 有很多问题,但最好避免)。

zsh

a=(1 2 3 4 5)
a[3]=() # remove the 3rd element
a[1,3]=() # remove the first 3 elements
a[-1]=() # remove the last element

(请注意,在 zsh 中,unset 'a[3]'实际上将其设置为空字符串以提高与 的兼容性ksh

yash

a=(1 2 3 4 5)
array -d a 3 # remove the 3rd element
array -d a 1 2 3 # remove the first 3 elements
array -d a -1 # remove the last element

在(不是与/fish相反的类似 Bourne 的 shell ):bashzsh

set a 1 2 3 4 5
set -e a[3] # remove the 3rd element
set -e a[1..3] # remove the first 3 elements
set -e a[-1] # remove the last element

es(基于rc,而不是类似 Bourne 的)

a = 1 2 3 4 5
a = $a(... 2 4 ...) # remove the 3rd element
a = $a(4 ...) # remove the first 3 elements
a = $a(... `{expr $#a - 1}) # remove the last element
# or a convoluted way that avoids forking expr:
a = $a(... <={@{*=$*(2 ...); return $#*} $a})

kshbash

如果您这样做,则可以将数组用作普通数组:

a=("${a[@]}")

在每次删除或插入操作之后,这可能会使索引列表不连续或不从 0 开始。另请注意ksh/bash数组从 0 开始,而不是 1 (除了$@(在某些方面))。

这实际上会整理元素并将它们按顺序移动到索引 0、1、2...。

另请注意,您需要引用以下number[i]内容:

unset 'number[i]'

否则,如果当前目录中unset numberi有一个文件被调用,那么它将有效地运行。numberi

相关内容