我需要从bash shell中的数组中删除一个元素。 一般来说,我只是这样做:
array=("${(@)array:#<element to remove>}")
不幸的是,我要删除的元素是一个变量,因此我无法使用上一个命令。 在这里举个例子:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
有什么想法吗?
答案 0 :(得分:119)
您可以在bash
和zsh
中使用以下内容:
$ array=(pluto pippo)
$ delete=(pluto)
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
如果需要删除多个元素:
...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
array=("${array[@]/$del}") #Quotes when working with strings
done
<强>买者强>
这种技术实际上会从元素中删除与$delete
匹配的前缀,而不一定是整个元素。
<强>更新强>
要真正删除确切的项目,您需要遍历数组,将目标与每个元素进行比较,然后使用unset
删除完全匹配。
array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
for i in "${!array[@]}"; do
if [[ ${array[i]} = "${delete[0]}" ]]; then
unset 'array[i]'
fi
done
done
请注意,如果执行此操作,并且删除了一个或多个元素,则索引将不再是连续的整数序列。
$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
简单的事实是,数组并非设计用作可变数据结构。它们主要用于在单个变量中存储项目列表,而不需要将字符作为分隔符(例如,存储可包含空格的字符串列表)。
如果间隙有问题,那么您需要重建阵列以填补空白:
for i in "${!array[@]}"; do
new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
答案 1 :(得分:20)
您可以构建一个没有不需要的元素的新数组,然后将其分配回旧数组。这适用于bash
:
array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
这会产生:
echo "${array[@]}"
pippo
答案 2 :(得分:6)
如果你知道它的位置,这是取消设置值的最直接方法。
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
答案 3 :(得分:3)
这是一个带有mapfile的单行解决方案:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
示例:
$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "^Claire\nSmith$")
$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"
Size: 5 Contents: Adam Bob David Eve Fred
这种方法通过修改/交换grep命令可以提供很大的灵活性,并且不会在数组中留下任何空字符串。
答案 4 :(得分:2)
要扩展上述答案,可以使用以下内容从数组中删除多个元素,而不进行部分匹配:
ARRAY=(one two onetwo three four threefour "one six")
TO_REMOVE=(one four)
TEMP_ARRAY=()
for pkg in "${ARRAY[@]}"; do
for remove in "${TO_REMOVE[@]}"; do
KEEP=true
if [[ ${pkg} == ${remove} ]]; then
KEEP=false
break
fi
done
if ${KEEP}; then
TEMP_ARRAY+=(${pkg})
fi
done
ARRAY=("${TEMP_ARRAY[@]}")
unset TEMP_ARRAY
这将产生一个包含以下内容的数组: (两个上三个三个四个“一个六个”)
答案 5 :(得分:1)
这是一个涉及bash变量间接和unset
的小函数(可能非常特定于bash)。它是一个通用的解决方案,不涉及文本替换或丢弃空元素,并且引用/空格等没有问题。
delete_ary_elmt() {
local word=$1 # the element to search for & delete
local aryref="$2[@]" # a necessary step since '${!$2[@]}' is a syntax error
local arycopy=("${!aryref}") # create a copy of the input array
local status=1
for (( i = ${#arycopy[@]} - 1; i >= 0; i-- )); do # iterate over indices backwards
elmt=${arycopy[$i]}
[[ $elmt == $word ]] && unset "$2[$i]" && status=0 # unset matching elmts in orig. ary
done
return $status # return 0 if something was deleted; 1 if not
}
array=(a 0 0 b 0 0 0 c 0 d e 0 0 0)
delete_ary_elmt 0 array
for e in "${array[@]}"; do
echo "$e"
done
# prints "a" "b" "c" "d" in lines
像delete_ary_elmt ELEMENT ARRAYNAME
一样使用它而没有任何$
sigil。切换== $word
== $word*
作为前缀匹配;使用${elmt,,} == ${word,,}
进行不区分大小写的匹配;等等,无论bash [[
支持什么。
它的工作原理是确定输入数组的索引并向后迭代它们(因此删除元素不会使迭代顺序失效)。要获取索引,您需要按名称访问输入数组,这可以通过bash变量间接x=1; varname=x; echo ${!varname} # prints "1"
来完成。
您无法按名称aryname=a; echo "${$aryname[@]}
访问数组,这会给您一个错误。你不能aryname=a; echo "${!aryname[@]}"
,这会给你变量aryname
的索引(尽管它不是数组)。什么工作是aryref="a[@]"; echo "${!aryref}"
,它将打印数组a
的元素,保留shell-word引用和空格,就像echo "${a[@]}"
一样。但这仅适用于打印数组的元素,而不是用于打印其长度或索引(aryref="!a[@]"
或aryref="#a[@]"
或"${!!aryref}"
或"${#!aryref}"
,它们都会失败)。< / p>
所以我通过bash间接名称复制原始数组,并从副本中获取索引。为了反向迭代索引,我使用C风格的循环。我也可以通过${!arycopy[@]}
访问索引并使用tac
将其反转来实现,cat
是一个转换输入行顺序的eval
。
没有变量间接的函数解决方案可能必须涉及public void clicked(View v) throws IOException{
TestMain example = new TestMain();
example.doGetRequest("http://www.vogella.com");
}
public class TestMain {
OkHttpClient client = new OkHttpClient();
// code request code here
String doGetRequest(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
,在这种情况下使用它可能安全也可能不安全(我无法告诉)。
答案 6 :(得分:1)
此答案特定于从大型数组删除多个值的情况,在这种情况下,性能至关重要。
最受欢迎的解决方案是(1)数组上的模式替换,或(2)遍历数组元素。第一个是快速的,但是只能处理具有不同前缀的元素,第二个具有O(n * k),n =数组大小,k =要删除的元素。关联数组是相对的新功能,最初发布问题时可能并不常见。
对于n和k较大的精确匹配情况,可以将性能从O(n k)提升到O(n + k log(k))。实际上,O(n)假设k比n低得多。大部分的加速是基于使用关联数组来标识要删除的项目。
性能(n数组大小,要删除的k值)。性能衡量用户时间的秒数
N K New(seconds) Current(seconds) Speedup
1000 10 0.005 0.033 6X
10000 10 0.070 0.348 5X
10000 20 0.070 0.656 9X
10000 1 0.043 0.050 -7%
正如预期的那样,current
解与N * K呈线性关系,而fast
解与K几乎呈线性关系,常数要低得多。由于额外的设置,fast
解决方案比k {= 1时的current
解决方案要慢一些。
“快速”解决方案:array =输入列表,delete =要删除的值列表。
declare -A delk
for del in "${delete[@]}" ; do delk[$del]=1 ; done
# Tag items to remove, based on
for k in "${!array[@]}" ; do
[ "${delk[${array[$k]}]-}" ] && unset 'array[k]'
done
# Compaction
array=("${array[@]}")
根据投票最多的答案,以current
解决方案为基准。
for target in "${delete[@]}"; do
for i in "${!array[@]}"; do
if [[ ${array[i]} = $target ]]; then
unset 'array[i]'
fi
done
done
array=("${array[@]}")
答案 7 :(得分:1)
POSIX shell脚本没有数组。
所以很可能你使用的是特定的方言,例如bash
,korn shell或zsh
。
因此,您现在的问题无法解答。
也许这适合你:
unset array[$delete]
答案 8 :(得分:0)
http://wiki.bash-hackers.org/syntax/pe#substring_removal
$ {PARAMETER#PATTERN}#从头开始删除
$ {PARAMETER ## PATTERN}#从头开始删除,贪婪的比赛
$ {PARAMETER%PATTERN}#从最后删除
$ {PARAMETER %% PATTERN}#从最后删除,贪婪匹配
为了执行完全删除元素,您必须使用if语句执行unset命令。如果您不关心从其他变量中删除前缀或关于支持数组中的空格,那么您可以删除引号并忘记for循环。
请参阅下面的示例,了解清理阵列的几种不同方法。
options=("foo" "bar" "foo" "foobar" "foo bar" "bars" "bar")
# remove bar from the start of each element
options=("${options[@]/#"bar"}")
# options=("foo" "" "foo" "foobar" "foo bar" "s" "")
# remove the complete string "foo" in a for loop
count=${#options[@]}
for ((i = 0; i < count; i++)); do
if [ "${options[i]}" = "foo" ] ; then
unset 'options[i]'
fi
done
# options=( "" "foobar" "foo bar" "s" "")
# remove empty options
# note the count variable can't be recalculated easily on a sparse array
for ((i = 0; i < count; i++)); do
# echo "Element $i: '${options[i]}'"
if [ -z "${options[i]}" ] ; then
unset 'options[i]'
fi
done
# options=("foobar" "foo bar" "s")
# list them with select
echo "Choose an option:"
PS3='Option? '
select i in "${options[@]}" Quit
do
case $i in
Quit) break ;;
*) echo "You selected \"$i\"" ;;
esac
done
输出
Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option?
希望有所帮助。
答案 9 :(得分:0)
实际上,我只是注意到shell语法有点内置的行为,当问题中提出的项目应该被移除时,允许轻松重建数组。
# let's set up an array of items to consume:
x=()
for (( i=0; i<10; i++ )); do
x+=("$i")
done
# here, we consume that array:
while (( ${#x[@]} )); do
i=$(( $RANDOM % ${#x[@]} ))
echo "${x[i]} / ${x[@]}"
x=("${x[@]:0:i}" "${x[@]:i+1}")
done
注意我们是如何使用bash的x+=()
语法构建数组的?
你实际上可以添加多个项目,一次添加整个其他数组的内容。
答案 10 :(得分:0)
使用unset
要删除特定索引处的元素,我们可以使用unset
然后复制到另一个数组。在这种情况下,不需要unset
。因为unset
没有删除元素,所以它只是将空字符串设置为数组中的特定索引。
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in "${arr[@]}"
do
arr2[$i]=$element
((++i))
done
echo "${arr[@]}"
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
输出
aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
使用:<idx>
我们也可以使用:<idx>
删除一些元素集。例如,如果我们要删除第一个元素,我们可以使用:1
,如下所述。
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
输出
bb cc dd ee
1st val is cc, 2nd val is dd
答案 11 :(得分:0)
要删除数组中的第一项
unset 'array[0]'
要删除数组中的最后一项
unset 'array[-1]'
答案 12 :(得分:0)
还有这种语法,例如如果要删除第二个元素:
array=("${array[@]:0:1}" "${array[@]:2}")
实际上是2个标签的串联。第一个从索引0到索引1(不包括索引),第二个从索引2到末尾。
答案 13 :(得分:0)
为避免使用unset
与数组索引发生冲突-有关更多信息,请参见https://stackoverflow.com/a/49626928/3223785和https://stackoverflow.com/a/47798640/3223785-将数组重新分配给它自己:ARRAY_VAR=(${ARRAY_VAR[@]})
。
#!/bin/bash
ARRAY_VAR=(0 1 2 3 4 5 6 7 8 9)
unset ARRAY_VAR[5]
unset ARRAY_VAR[4]
ARRAY_VAR=(${ARRAY_VAR[@]})
echo ${ARRAY_VAR[@]}
A_LENGTH=${#ARRAY_VAR[*]}
for (( i=0; i<=$(( $A_LENGTH -1 )); i++ )) ; do
echo ""
echo "INDEX - $i"
echo "VALUE - ${ARRAY_VAR[$i]}"
done
exit 0
[Ref .: https://tecadmin.net/working-with-array-bash-script/]
答案 14 :(得分:0)
在ZSH中,这很简单(注意,为了便于理解,这使用了比必要的更多bash兼容语法):
# I always include an edge case to make sure each element
# is not being word split.
start=(one two three 'four 4' five)
work=(${(@)start})
idx=2
val=${work[idx]}
# How to remove a single element easily.
# Also works for associative arrays (at least in zsh)
work[$idx]=()
echo "Array size went down by one: "
[[ $#work -eq $(($#start - 1)) ]] && echo "OK"
echo "Array item "$val" is now gone: "
[[ -z ${work[(r)$val]} ]] && echo OK
echo "Array contents are as expected: "
wanted=("${start[@]:0:1}" "${start[@]:2}")
[[ "${(j.:.)wanted[@]}" == "${(j.:.)work[@]}" ]] && echo "OK"
echo "-- array contents: start --"
print -l -r -- "-- $#start elements" ${(@)start}
echo "-- array contents: work --"
print -l -r -- "-- $#work elements" "${work[@]}"
结果:
Array size went down by one:
OK
Array item two is now gone:
OK
Array contents are as expected:
OK
-- array contents: start --
-- 5 elements
one
two
three
four 4
five
-- array contents: work --
-- 4 elements
one
three
four 4
five
答案 15 :(得分:0)
如果有人发现自己需要记住set -e或set -x值并能够还原它们,请查看使用第一个数组删除解决方案来管理其自身堆栈的要点: >
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
答案 16 :(得分:-1)
这是一个快速而肮脏的解决方案,可以在简单的情况下工作但如果(a)$delete
中有正则表达式特殊字符,或者(b)任何项目中都有任何空格,则会中断。从:
array+=(pluto)
array+=(pippo)
delete=(pluto)
删除与$delete
完全匹配的所有条目:
array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)
导致
echo $array
- &gt; pippo,并确保它是一个数组:
echo $array[1]
- &gt;皮波
fmt
有点模糊:fmt -1
包裹在第一列(将每个项目放在自己的行上。这就是空格中的项目出现问题的地方。){{ 1}}将它展开回一行,放回项目之间的空格。还有其他方法可以做到这一点,例如fmt -999999
。
附录:如果您只想删除第一个匹配项,请使用sed,如here所述:
xargs
答案 17 :(得分:-1)
如下:
array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
答案 18 :(得分:-1)
我的工作是:
array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"
BAM,该项目已被移除。
答案 19 :(得分:-2)
#/bin/bash
echo "# define array with six elements"
arr=(zero one two three 'four 4' five)
echo "# unset by index: 0"
unset -v 'arr[0]'
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
arr_delete_by_content() { # value to delete
for i in ${!arr[*]}; do
[ "${arr[$i]}" = "$1" ] && unset -v 'arr[$i]'
done
}
echo "# unset in global variable where value: three"
arr_delete_by_content three
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
echo "# rearrange indices"
arr=( "${arr[@]}" )
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
delete_value() { # value arrayelements..., returns array decl.
local e val=$1; new=(); shift
for e in "${@}"; do [ "$val" != "$e" ] && new+=("$e"); done
declare -p new|sed 's,^[^=]*=,,'
}
echo "# new array without value: two"
declare -a arr="$(delete_value two "${arr[@]}")"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
delete_values() { # arraydecl values..., returns array decl. (keeps indices)
declare -a arr="$1"; local i v; shift
for v in "${@}"; do
for i in ${!arr[*]}; do
[ "$v" = "${arr[$i]}" ] && unset -v 'arr[$i]'
done
done
declare -p arr|sed 's,^[^=]*=,,'
}
echo "# new array without values: one five (keep indices)"
declare -a arr="$(delete_values "$(declare -p arr|sed 's,^[^=]*=,,')" one five)"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done
# new array without multiple values and rearranged indices is left to the reader