从数组中删除最后一个元素。

51

我想删除数组中的最后一个元素,并且当我使用 ${#array[@]} 时,希望该数组显示它已经少了1个元素。这是我目前正在使用的代码行:

unset GreppedURLs[${#GreppedURLs[@]} -1]

请纠正我并向我展示正确的方法。

6个回答

72
你所得到的答案(几乎)是正确的对于非稀疏索引数组¹:
unset 'arr[${#arr[@]}-1]'

Bash 4.3 或更高版本添加了这种新的语法来执行相同操作。
 unset arr[-1]
(注意单引号:它们可以防止路径名扩展)。
演示:
arr=( a b c )
echo ${#arr[@]}

3

for a in "${arr[@]}"; do echo "$a"; done
a
b
c
unset 'arr[${#arr[@]}-1]'
for a in "${arr[@]}"; do echo "$a"; done
a
b

妙语

echo ${#arr[@]}
2

(GNU bash,版本4.2.8(1)-release (x86_64-pc-linux-gnu))


¹ @Wil 提供了一个出色的答案,适用于各种类型的数组


适用于GNU bash,版本3.1.0(3)-release(sparc-sun-solaris2.9)。 - 0zkr PM
@sehe 原问题的数据集没有提供,这就是为什么我说你的答案是错误的 - 因为它对数据集做出了盲目的假设...如果你想把我的评论称为“竞争修辞”,那好吧,哈哈...你有权像那样说话。 - Wil
当然,没有必要通过编写索引怪兽 arr[${#arr[@]}-1] 来破坏代码的可读性。Bash 数组可以被视为“循环”结构,也就是说,第 -1 个元素和最后一个元素是相同的。因此,可以使用 unset arr[-1] - Géza Török
1
@GézaTörök “当然”,这个功能是从zsh复制过来的。它很好用,但请注意,这是在bash 4.3之后添加的(比这个答案写得晚了几年)。并不是每个系统都有最新的bash版本。例如,Ubuntu LTS几乎没有(只有4.4版本)。更新答案文本。 - sehe
1
我非常想强调答案注释的重要性:使用带引号的变量以避免路径名扩展!并且进一步澄清,方括号[]是特殊的全局字符(当然取决于您的bash glob选项,例如在脚本中与交互模式中)。 - J. Doe
显示剩余3条评论

16

-1之前必须去掉空格。


14

如果您想得到一个不会吃掉您小猫的答案,请尝试以下方法:

array=([1]=1 {2..5} [10]=6);
# declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6}")'
index=("${!array[@]}");
# declare -a index='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="10")'
unset 'array[${index[@]: -1}]';
# declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5")'

接下来,我将介绍一个更简单的答案,可以满足您的需求,但有一个警告:

array=([1]=1 {2..5} [10]=6);
# declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6}")'
array=("${array[@]::${#array[@]}-1}");
# declare -a array='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'

这个版本采取了一种捷径。它重新索引了数组并删除了最后一个元素。不幸的是,您还可以看到索引没有被维护。值及其顺序已被维护。如果您不关心索引,则这可能是您想要的答案。

上述两个答案也适用于bash 4关联数组

--

所选答案不安全。以下是一个例子:
array=([1]=1 {2..5} [10]=6);
# declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [5]="5" [10]="6")'
unset 'arr[${#arr[@]}-1]';
# declare -a array='([1]="1" [2]="2" [3]="3" [4]="4" [10]="6")'

好的,正如你所看到的,它正在取消索引为5的元素,因为它错误地计算了数组的最后一个元素的索引。它失败是因为它基于一种假设来操作,即所有数组都是从零开始的,而不是稀疏数组。这个答案将在以零以外的任何其他数字开头的数组上失败,在稀疏数组上也会失败,并且显然对于具有“fubar”作为最后一个元素的关联数组也必须失败。

2
在Bash≥4中的标准方法是只需使用unset 'arr[-1]'。顺便说一下,你的答案也是错误的:在你的unset语句中缺少引号,因此你的命令会受到路径名扩展的影响。 - gniourf_gniourf
@gniourf_gniourf 对于关联数组, 这会做正确的事情吗?那将是最直接的答案。 - sehe
2
@sehe:当然,对于关联数组它不能按预期工作(它将取消键为“-1”的字段)。但这并不是真正的问题:从关联数组中删除“最后”一个元素并没有什么意义(即:没有用处);在关联数组的情况下,“最后”一个元素是什么? - gniourf_gniourf
对于那些不理解的人,我认为@Wil的评论是回复这个评论的。我很感激这里所有的好想法和信息。 - sehe
你怎么样引用数组扩展来填充index?你声称你的方法不会吃掉我的小猫,但是它确实这样做了。 declare -A array=( [\*]=you_killed_my_kittens [cat]=i_love_kittens ); index=(${!array[@]}); unset 'array[${index[@]: -1}]'; declare -p array. 噢~~~~ - gniourf_gniourf
显示剩余2条评论

9

对于任何索引数组(无论稀疏还是不稀疏),自bash 4.3+(和ksh93+)以来,这是最简单的解决方案:

unset 'array[-1]'

如果-1是算术表达式或变量,则需要引号来避免在bash中进行shell扩展。下面这种写法也是正确的:

a=3; unset 'arr[ a - 4 * 1 ]'

如果未加引号(''),则无法正常工作,因为星号将会扩展为当前工作目录中的文件列表($pwd)。针对IT技术相关内容,以下是适用于旧版bash的解决方案:这适用于非稀疏数组,自bash 3.0版本起可使用:

unset 'arr[ ${#arr[@]}-1 ]'

例子:

$ arr=( {a..i} ); declare -p arr  
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [7]="h")
$ unset 'arr[ ${#arr[@]}-1 ]'; declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g")

这种方法不适用于稀疏数组(带有一些空洞):

$ arr=( {a..g} [9]=i ); declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [9]="i")
$ unset 'arr[ ${#arr[@]}-1 ]'; declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g" [9]="i")

这是因为元素的计数(${#arr[@]})是8,而8-17
因此,该命令将取消设置不存在的arr[7]。什么也没做。
一种解决方法(也适用于关联数组,在未排序列表中可以理解为“最后一个元素”)是生成一个新的索引数组。
然后使用最后一个索引来取消设置该元素。
假设arr已经被定义(适用于bash 3.0+):
$ index=( "${!arr[@]}" )          # makes index non-sparse.
$ unset 'arr[${index[@]}-1]'      # unset the last index.
$ declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f" [6]="g")

一种稍微更加便携(适用于ksh93),看起来不太美观的解决方案是:
$ arr=( {a..e} [9]=i )
$ index=( "${!arr[@]}" )
$ unset "arr[  ${index[${#index[@]}-1]}  ]"   # Yes, double quotes.
$ declare -p arr
declare -a arr=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e")   

或者(同样,对于ksh使用双引号):
$ unset "arr[${index[@]: -1}]"

如果要避免空格和负数,请将其设置为变量:
$ a="-1"; unset "arr[${index[@]:a}]"

我喜欢 unset 'array[-1]' 的简洁性,但不幸的是它在我的操作系统中最新版本的bash v4中不是一个有效的命令。不过我会记住它以备将来之需。 - Cameron Hudson
@CameronHudson 然后使用unset 'array [ $ {index [@]}-1]',自从bash 3.0以后(在此答案中列出)。 - user8017719

1
以下内容适用于Mac/bash@3.x和Linux(ubuntu/bash@4.x)。
unset arr[$[${#arr[@]}-1]] # non-sparse array only

更详细地说:

len=${#arr[@]}
idx=$[$len-1]    # <=> $(($len-1))
unset arr[$idx]

$[ ] 语法已经过时。 - Sapphire_Brick
GNU bash 3.2.57(1)-release (x86_64-apple-darwin21) 的工作案例 - Nikolay Baranenko

1
在你的函数中,你可以添加以下内容:
target="${@:$(($#)):1}"
set -- "${@:1:$(($#-1))}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接