Arrays 从Bash数组中删除元素
我需要从bashshell中的数组中删除一个元素。 一般来说,我只想:Arrays 从Bash数组中删除元素,arrays,bash,variables,Arrays,Bash,Variables,我需要从bashshell中的数组中删除一个元素。 一般来说,我只想: array=("${(@)array:#<element to remove>}") 有什么想法吗?POSIX shell脚本没有数组 因此,您很可能正在使用一种特定的方言,例如bash、kornshell或zsh 因此,到目前为止,你的问题无法得到回答 也许这对你有用: unset array[$delete] 以下内容可以在bash和zsh中正常工作: $ array=(pluto pippo) $ de
array=("${(@)array:#<element to remove>}")
有什么想法吗?POSIX shell脚本没有数组 因此,您很可能正在使用一种特定的方言,例如
bash
、kornshell或zsh
因此,到目前为止,你的问题无法得到回答
也许这对你有用:
unset array[$delete]
以下内容可以在
bash
和zsh
中正常工作:
$ array=(pluto pippo)
$ delete=pluto
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
如果需要删除多个元素:
...
$ 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]} = $target ]]; 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
您可以构建一个没有不需要的元素的新数组,然后将其分配回旧数组。这适用于
bash
:
$ array=(pluto pippo)
$ delete=pluto
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
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
要扩展上述答案,可以使用以下方法从数组中删除多个元素,而无需进行部分匹配:
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
这将生成一个包含以下内容的数组:
(二一二三三四“一六”)在ZSH中,这非常简单(注意,为了便于理解,这使用了比必要时更多的bash兼容语法):
结果:
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
我所做的是:
array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"
BAM,该项已删除。下面是一个带有mapfile的单行解决方案:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
$mapfile-d$'\0'-t arr<这是一个快速而肮脏的解决方案,在简单的情况下可以工作,但如果(a)在$delete
中有正则表达式特殊字符,或者(b)在任何项中都有空格,则会中断。首先是:
array+=(pluto)
array+=(pippo)
delete=(pluto)
删除所有完全匹配的条目$Delete
:
array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)
导致
echo$array
->pippo,并确保它是一个数组:
echo$array[1]
->pippo
fmt
有点晦涩:fmt-1
在第一列换行(将每个项目放在自己的行上。这就是空格中的项目出现问题的地方。)fmt-999999
将其展开回一行,将项目之间的空格放回。还有其他方法可以做到这一点,例如xargs
附录:如果只想删除第一个匹配项,请使用sed,如下所述:
${PARAMETER#PATTERN}#从开头删除
${PARAMETER###PATTERN}#从开始删除,贪婪匹配
${PARAMETER%PATTERN}#从末尾删除
${PARAMETER%%PATTERN}#从末尾删除,贪婪匹配
为了执行完整的remove元素,必须使用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
希望这会有所帮助。实际上,我刚刚注意到shell语法有一个内置的行为,当问题中提出一个项目应该被删除时,它允许轻松地重建数组
#让我们设置一组要消费的物品:
x=()
对于((i=0;i这里有一个(可能非常特定于bash)的小函数,它涉及bash变量间接寻址和unset
;它是一个通用的解决方案,不涉及文本替换或丢弃空元素,并且在引用/空白等方面没有问题。
像使用delete_-ary_-elmt元素ARRAYNAME
一样使用它,而不使用任何$
符号。为前缀匹配切换=$word*
;为不区分大小写的匹配使用${elmt,,}=${word,}
等,无论bash[
支持什么
它的工作原理是确定输入数组的索引并向后迭代(因此删除元素不会扰乱迭代顺序)。要获得索引,需要按名称访问输入数组,这可以通过bash变量indirectionx=1;varname=x;echo${!varname}打印“1”
您不能按名称访问数组,例如aryname=a;echo“${$aryname[@]}
,这会给您一个错误。您不能执行aryname=a;echo“${!aryname[@]}”
,这会给您变量aryname
(尽管它不是数组)的索引。工作的是aryref=“a[@]”echo“$!aryref}”“
,它将打印数组的元素a
,保留shell单词引用和空白,就像echo“${a[@]}”
一样。但这只适用于打印数组的元素,而不适用于打印数组的长度或索引(aryref=“!a[@]”
或aryref=“#a[@]”或或“${aryref
或“${#!aryref}”
,它们都会失败)
因此,我通过bash间接寻址按名称复制原始数组,并从副本中获取索引。要反向迭代索引,我使用C样式的for循环。我还可以通过${!arycopy[@]}访问索引
并使用tac
将其反转,这是一种cat
,可改变输入行顺序
没有变量间接寻址的函数解决方案可能必须涉及eval
,在这种情况下使用它可能安全,也可能不安全(我说不出来)。类似于:
array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
如果知道某个值的位置,这是取消设置该值最直接的方法
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?
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
array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
#/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
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
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
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
unset 'array[0]'
unset 'array[-1]'
array=("${array[@]:0:1}" "${array[@]:2}")
#!/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
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%
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[@]}")
for target in "${delete[@]}"; do
for i in "${!array[@]}"; do
if [[ ${array[i]} = $target ]]; then
unset 'array[i]'
fi
done
done
array=("${array[@]}")