Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 从Bash数组中删除元素_Arrays_Bash_Variables - Fatal编程技术网

Arrays 从Bash数组中删除元素

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

我需要从bashshell中的数组中删除一个元素。 一般来说,我只想:

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变量indirection
x=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[@]}")