从数组shell中移除元素

我需要从bash shell中的数组中删除一个元素。 一般我只是做:

array=("${(@)array:#<element to remove>}") 

不幸的是我想删除的元素是一个variables,所以我不能使用以前的命令。 在这里举一个例子:

 array+=(pluto) array+=(pippo) delete=(pluto) array( ${array[@]/$delete} ) -> but clearly doesn't work because of {} 

任何想法?

bashzsh ,以下方法是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了匹配$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") 

简单的事实是,数组并不是devise用作可变数据结构的。 它们主要用于将项目列表存储在单个variables中,而不需要浪费字符作为分隔符(例如,存储可包含空格的string列表)。

如果差距是一个问题,那么你需要重build数组来弥补差距:

 for i in "${!array[@]}"; do new_array+=( "${array[i]} ) done array=("${new_array[@]}") unset new_array 

您可以build立一个没有不需要的元素的新数组,然后将其分配回旧数组。 这在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 

您可以将要销毁的元素设置为空string,将被视为已删除。

以下示例显示如何删除abcd数组中的bstring:

 $ array=(abcd) $ echo ${array[@]} abcd $ for i in ${!array[@]} ; do # iterate over array indexes if [ ${array[$i]} = 'b' ] ; then # if it's the value you want to delete array[$i]='' # set the string as empty at this specific index fi done $ echo ${array[@]} acd 

注意,这个解决scheme即使在像sh这样的POSIX shell中也能工作。

POSIX shell脚本没有数组。

所以很可能你正在使用特定的方言,如bash ,korn shell或zsh

所以,你现在的问题是不能回答的。

也许这对你有用:

 unset array[$delete] 

为了扩展上面的答案,可以使用以下内容从数组中删除多个元素,而不进行部分匹配:

 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中这很容易:

 # 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 

实际上,我刚刚注意到,shell语法有一些内置的行为,如在问题中提出的,应该删除一个项目,这样可以方便地重build数组。

 # 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+=()语法构造数组?

你实际上可以添加多个项目,即一个整个其他数组的内容。

这里是一个(可能是非常bash特定的)小function涉及bashvariables间接和未unset ; 这是一个通用的解决scheme,不涉及文本replace或丢弃空的元素,并没有问题引用/空格等

 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 de 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没有任何$印记。 切换== $word== $word*为前缀匹配; 使用${elmt,,} == ${word,,}来区分大小写匹配; 等等,无论bash [[支持。

它通过确定input数组的索引并向后迭代来工作(所以删除元素并不会影响迭代次序)。 为了获得索引,你需要通过名称访问input数组,这可以通过bashvariables间接x=1; varname=x; echo ${!varname} # prints "1"来完成x=1; varname=x; echo ${!varname} # prints "1" x=1; varname=x; echo ${!varname} # prints "1" x=1; varname=x; echo ${!varname} # prints "1"

你不能像aryname=a; echo "${$aryname[@]}那样通过名字来访问数组aryname=a; echo "${$aryname[@]} aryname=a; echo "${$aryname[@]} ,这会给你一个错误,你不能做aryname=a; echo "${!aryname[@]}" ,这会给你variablesaryname的索引(尽pipe它不是一个数组),什么工作是aryref="a[@]"; echo "${!aryref}" ,这将打印数组的元素a ,保持贝壳字引用和空白完全一样echo "${a[@]}"aryref="!a[@]"或者aryref="#a[@]"或者"${!!aryref}" echo "${a[@]}" ,但这只适用于打印数组的元素。 "${!!aryref}"或者"${#!aryref}" ,他们都失败了)。

所以我通过bash间接复制原始数组的名称,并从副本获取索引。 要反向遍历索引,我使用C风格for循环。 我也可以通过${!arycopy[@]}来访问索引,并使用tac (这是一个围绕input行顺序的cat来反转它们。

没有可变间接的函数解决scheme可能会涉及eval ,在这种情况下使用可能是安全的,也可能不安全(我不明白)。

http://wiki.bash-hackers.org/syntax/pe#substring_removal

$ {PARAMETER#PATTERN}#从开始删除

$ {PARAMETER ## PATTERN}#从头开始删除,贪婪的匹配

$ {PARAMETER%PATTERN}#从结尾删除

$ {PARAMETER %% PATTERN}#从结尾删除,贪婪的匹配

为了做一个完整的删除元素,你必须用一个if语句来做一个unset命令。 如果你不关心从其他variables中删除前缀或者支持数组中的空格,那么你可以放弃引号而忘记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? 

希望有所帮助。

这是一个mapfile的单行解决scheme:

 $ mapfile -t <array> < <(printf '%s\n' "${arr[@]}" | grep -xv "<element>") 

例:

 $ arr=("Adam" "Bob" "Claire" "David" "Eve" "Fred") $ echo "Size: ${#arr[*]} Contents: ${arr[*]}" Size: 6 Contents: Adam Bob Claire David Eve Fred $ mapfile -t arr < <(printf '%s\n' "${arr[@]}" | grep -xv "Claire") $ echo "Size: ${#arr[*]} Contents: ${arr[*]}" Size: 5 Contents: Adam Bob David Eve Fred 

这种方法通过修改/交换grep命令来提供很大的灵活性,并且不会在数组中留下任何空string。

我所做的是:

 array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")" 

BAM,该项目被删除。

这是一个简单而快速的解决scheme,可以在简单的情况下工作,但如果(a) $delete有正则expression式特殊字符,或者(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 fmt -999999解开它回到一行,项目。 还有其他方法可以做到,比如xargs

附录:如果要删除第一个匹配项,请使用sed,如下所述:

 array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`) 

感觉如何?

 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 iv; 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 

只删除与您的项目相匹配的内容:

 user@pc:~$ itm="two" #Item to be deleted user@pc:~$ n=(one two three twentytwo) #The array of items user@pc:~$ n=(`echo ${n[@]} | sed -r "s/\<${itm}\>//g"`) user@pc:~$ echo ${n[@]} one three twentytwo