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


12
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
考虑到此问题所隐含的编码工作类型,您应该警告:请参阅我的脚本是否有问题或Bash是否比Python慢​​?
通配符

Answers:


21

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)

正如Toby Speight在评论中所建议的那样,一种删除第四个元素并对其余元素重新编号的方法只需一步即可:

$ 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 array numbers:它以元素0开头的前四个元素。

类似地,${numbers[@]:5}slice array 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]销毁index处的数组元素subscript。如上所述,对索引数组的负下标进行解释。必须注意避免由路径名扩展引起的有害副作用。 unset name,其中name是一个数组,或unset name[subscript],其中subscript*@,删除整个阵列。


1
请注意,shell数组语法实际上只是一种使其易于处理名称相似的变量的方法。本身没有数组。实际上,在编写a=()完变量之后,a直到您实际上将其分配给它的索引之一之前,该变量仍然是未定义的。
chepner's

@ John1024:谢谢您的回答。您能否将其扩展为包括为达到预期结果的建议答案?
Anthony Webber

@AnthonyWebber当然。我在答案中添加了一部分,以说明如何强制对索引重新编号。
John1024年

2
只需提及一种替代方法(可能更适合某些代码):而不是unset numbers[4]使用切片来分配整个数组,即numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")(我会作为回答,但没有时间进行适当解释)。
Toby Speight

@ John1024:谢谢你这样做。还有托比(Thnx Toby):)
安东尼·韦伯

5

bash像in ksh中的数组并不是真正的数组,它们更像是键限于正整数的关联数组(或所谓的稀疏数组)。对于真正的阵列外壳,你可以看看像贝壳rcesfishyashzsh(甚至是csh/ tcsh尽管这些炮弹有这么多的问题,他们是更好的避免)。

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(不是类似于bash/ 的Bourne壳zsh)中:

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

in 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]in:

unset 'number[i]'

否则,将被视为当前目录中unset numberi有一个称为的文件numberi

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.