【发布时间】:2018-05-24 00:31:03
【问题描述】:
注意:此文本的永久副本可在 https://bpaste.net/raw/20a08beae676 上获得
这是一个关于如何在 for 循环中正确地从数组中删除元素的自答教程(在这种情况下,我将假设你在数组样式中使用 for i for 循环)
注意:这适用于当您想从 for 循环使用的数组中添加或删除元素时,例如“for i in ${!apples[@]}”;做废话;完成”如果我盲目地修改苹果变量,我将不会被相应地修改,如果你试图改变我,它不会起作用,因为这种 for 循环的风格不允许这样做,这是解决这个问题的方法
为此,我们将使用 https://stackoverflow.com/a/17533525/8680581 的编辑版本
重要
您要做的第一件事就是将 for 循环更改为 C 风格的等价物,这将使您能够完成这项工作:
for (( i=0; i<${#li[*]}; i++ ));
这相当于:
for i in ${!li[*]};
功能:
remove_array() {
if [[ $1 == "-v" ]]
then
verbose=1
shift 1
fi
wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=$1
shift 1
wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes=($@)
if [[ ! -z $verbose ]]
then
echo "old array is $(eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array")"
fi
for i in ${wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes[@]}
do
if [[ ! -z $verbose ]]
then
echo "unsetting index $i"
fi
eval "unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]"
done
eval "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\"\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\")"
if [[ ! -z $verbose ]]
then
echo "new array is $(eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array")"
fi
if [[ ! -z $verbose ]]
then
unset verbose
fi
unset wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes
}
对于那些认为这个函数过于复杂的人来说,这就是它看起来像非随机变量和冗长的东西:
函数(非随机且无详细选项):
remove_array() {
a_array=$1
shift 1
a_indexes=($@)
for i in ${a_indexes[@]}
do
eval "unset $a_array[i]"
done
eval "$a_array=(\"\${$a_array[@]}\")"
unset a_array a_indexes
}
代码:
li=(pi go dl og wa)
for (( i=0; i<${#li[*]}; i++ ));
do
echo "i before: $i"
echo "is array \"li\" index \"$i\" contents \"${li[i]}\" equal to \"go\" or \"og\"?"
if [[ "${li[i]}" == "go" || "${li[i]}" == "og" ]]
then
remove_array -v li $i
i=$(($i-1))
echo "setting i to $i here should cause i to repeat $i and check for \"go\" or \"og\" again"
fi
echo "i after: $i"
done
输出:
i before: 0
is array "li" index "0" contents "pi" equal to "go" or "og"?
i after: 0
i before: 1
is array "li" index "1" contents "go" equal to "go" or "og"?
old array is declare -a li=([0]="pi" [1]="go" [2]="dl" [3]="og" [4]="wa")
unsetting index 1
new array is declare -a li=([0]="pi" [1]="dl" [2]="og" [3]="wa")
setting i to 0 here should cause i to repeat 0 and check for "go" or "og" again
i after: 0
i before: 1
is array "li" index "1" contents "dl" equal to "go" or "og"?
i after: 1
i before: 2
is array "li" index "2" contents "og" equal to "go" or "og"?
old array is declare -a li=([0]="pi" [1]="dl" [2]="og" [3]="wa")
unsetting index 2
new array is declare -a li=([0]="pi" [1]="dl" [2]="wa")
setting i to 1 here should cause i to repeat 1 and check for "go" or "og" again
i after: 1
i before: 2
is array "li" index "2" contents "wa" equal to "go" or "og"?
i after: 2
深入概述
代码
首先我们启动循环
for (( i=0; i<${#li[*]}; i++ ));
do
然后我们设置子代码激活的条件
if [[ "${li[i]}" == "go" || "${li[i]}" == "og" ]]
如果数组“li”索引 $i 的内容等于“go”或“og”,这将激活
接下来我们做一些事情,在这种情况下,我们从数组“li”中删除“go”或“og”,这样它就不会在 i 减少时被无休止地触发
then
remove_array -v li $i
remove_array -v li $i 将从数组“li”中删除索引(或多个索引,如果指定了多个)$i(如变量 i 的值,而不是字面上的“$i”)然后它会缩小相应的数组,而不是将索引留空,这通常是不希望的
然后,一旦完成,我们会减少 $i 以说明数组大小的减少,否则它将跳过数组元素,就好像数组根本没有减少(这很糟糕)
i=$(($i-1))
这会将 i 设置为 i 减去 1 的值,例如
i = 3
考虑到扩展和变量替换,它看起来像这样
i=$((3-1))
>
i=$(3 minus 1) # note "minus" is not a real command nor valid syntax
>
3 - 1 = 2
>
i=2
然后我们结束循环
fi
done
当 i 减小时,它将在 for 循环 "i++" 中生效
2++
>
3
>
i-1
>
i=2
>
2++
函数
首先,如果需要,我们为详细输出设置一个小的详细标志,并检查位置参数 1 是否等于
-v
函数的(注意位置参数如下:表示非文字提示,但旨在根据可接受的内容提示它可以是什么)
(<function or command> arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9 arg10 arg11 and so on for as many characters as ur terminal will allow in a single line)
if [[ $1 == "-v" ]]
then
如果为真,我们将设置一个变量
verbose=1
然后我们将位置参数移动 1,使 arg1 变为 arg0,arg2 变为 arg1,arg3 变为 arg2,依此类推
shift 1
并完成设置
fi
然后我们对“array”变量和“index”数组进行半随机化,然后我们将_array(表示长半随机字符串)设置为函数的位置参数1
wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=$1
请注意,由于数组名称不太可能包含需要引用的字符,因此我们不会引用位置参数 1
然后我们将位置参数移动 1
shift 1
然后我们将 *_index 设置为所有位置参数以允许指定多个索引,并将其设为数组
wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes=($@)
然后我们测试 $verbose 是否存在
if [[ ! -z $verbose ]]
如果是这样,打印一些东西
then
echo "old array is $(eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array")"
fi
注意我们如何使用 eval,eval 可用于执行由于命令本身实际上依赖于某种变量而无法执行的命令,请注意 eval 工作(根据我的理解)首先执行ecko/printf 然后执行结果(很像
echo "ls /" | bash -
只有它不在子shell中执行,因此具有能够使用脚本的优势,并且它的用例有很多可能性,例如你可以使用它来动态生成一个依赖的if语句在一个数组上,关于有多少 if/elif 语句将节省大量时间,特别是如果它需要为许多函数完成并且数组内容未知(因为您每次都需要更改每个 if 语句想要添加或删除一个需要/不需要的变量,例如您观看的电影列表中的标题,或者要以复杂顺序检查的网站列表,很容易最终变成数千行代码,只是为了检查它们全部在每个函数中,使用 eval 简化为几行代码模板)
eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array"
在这个评估中,它评估以下内容,假设 *_array 包含“array_mine”
eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array"
>
eval "declare -p array_mine"
>
declare -p array_mine
然后执行declare -p array_mine
接下来我们遍历索引数组并取消设置每个索引
for i in ${wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes[@]}
do
if [[ ! -z $verbose ]]
then
echo "unsetting index $i"
fi
eval "unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]"
done
在这个 eval 中它是不同的但仍然是相同的概念,我们再次假设 *_array 是 array_mine
eval "unset $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[i]"
>
eval "unset array_mine[i]"
>
unset array_mine[i]
请注意,由于“i”本身并未被明确提及为变量,因此 eval 不会对其进行评估,但如果它是 eval “unset array_mine[$i]”,则它是无用的,因为 for 循环将 i 评估为它是索引值的参数,直接是数字
下一个循环结束
done
然后我们收缩数组,这个比较容易做到
eval "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\"\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\")"
替换到位:
eval "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\"\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\")"
>
eval "array_mine=(\"\${array_mine[@]}\")" # notice how we backslash the quotes and the array, this is done to prevent them from being evaluated or unquoting the quote thus making anything after the quote be interperated as literal command
>
array_mine=("${array_mine[@]}")
如果您想知道如果没有添加 \ 会发生什么
$ <eval or echo or printf> "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=("${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}")"
bash: "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=("${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}")": bad substitution
$ <eval or echo or printf> "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=(\"${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}\")"
bash: $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=("${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}"): bad substitution
$ <eval or echo or printf> "$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array=("\${$wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array[@]}")"
li=(${li[@]})
注意它在最后一个打印它时如何不带引号但似乎打印它即使 "..."..."..." 应该取消引用然后重新引用
然后结束我们打印最后的详细输出然后取消设置我们的变量并结束函数,现在我确定你们都知道 eval 是如何工作的(至少对于简单的东西,因为 eval 可以以非常复杂的方式使用)
if [[ ! -z $verbose ]]
then
echo "new array is $(eval "declare -p $wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array")"
fi
if [[ ! -z $verbose ]]
then
unset verbose
fi
unset wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_array wbgjsgvyueswbgbaeswgrvbseurbhguyewrhbgvuesw4rgyuvaifewbyguvaewrusgbfvauq3wegbvyuaevr_indexes
}
remove_array 函数的示例输出(详细和非详细):
$ array=(1 2 3 4 5 6 8 9 10 7)
$ remove_array -v array 5 2
old array is
declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="8" [7]="9" [8]="10" [9]="7")
unsetting index 5
unsetting index 2
new array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="8" [5]="9" [6]="10" [7]="7")
$ declare -p array
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="8" [5]="9" [6]="10" [7]="7")
$ array=(1 2 3 4 5 6 8 9 10 7)
$ remove_array array 1 8 3
$ declare -p array
declare -a array=([0]="1" [1]="3" [2]="5" [3]="6" [4]="8" [5]="9" [6]="7")
$
注意这个可以递归使用(比如删除每隔一个索引,eg
$ array=(1 2 3 4 5 6 8 9 10 7)
$ remove_array -v array 2
old array is
declare -a array=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="8" [7]="9" [8]="10" [9]="7")
unsetting index 2
new array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="6" [5]="8" [6]="9" [7]="10" [8]="7")
$ remove_array -v array 4
old array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="6" [5]="8" [6]="9" [7]="10" [8]="7")
unsetting index 4
new array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="8" [5]="9" [6]="10" [7]="7")
$ remove_array -v array 6
old array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="8" [5]="9" [6]="10" [7]="7")
unsetting index 6
new array is
declare -a array=([0]="1" [1]="2" [2]="4" [3]="5" [4]="8" [5]="9" [6]="7")
$
等等)
(虽然这样做很危险,因为可能会删除想要的数组索引)
也适用于带空格的数组
$ array=("1 2" "3 4" "5 6" "8 9" "10 7" 8 9 7 3)
$ remove_array -v array 3 7
old array is
declare -a array=([0]="1 2" [1]="3 4" [2]="5 6" [3]="8 9" [4]="10 7" [5]="8" [6]="9" [7]="7" [8]="3")
unsetting index 3
unsetting index 7
new array is
declare -a array=([0]="1 2" [1]="3 4" [2]="5 6" [3]="10 7" [4]="8" [5]="9" [6]="3")
$
作为旁注,这里有两个评估一些打印代码的 eval 语句
eval "$(echo ls /)"
test_func() {
directories=( \
bin \
lib \
usr \
proc \
)
printf "dirs=(
/tmp
/dev
"
for i in ${!directories[@]}
do
printf '%b' "
/${directories[i]}
"
done
printf ")
"
printf 'dirs_filtered() {
for i in ${!dirs[@]}
do
if [[ ${dirs[i]} =~ "/tm" ]]
then
printf "${dirs[i]} detected
"
elif [[ ${dirs[i]} =~ "/proc" ]]
then
printf "${dirs[i]} detected and is a special file system
"
fi
done
}
dirs_filtered
'
}
eval "$(test_func)"
【问题讨论】:
-
它旨在作为这个问题的自我回答/教程,而不是作为问题本身
-
@ClarkKent 这个网站上的问题应该是一个问题。这看起来不像一个问题。这看起来像一个教程。请阅读How to Ask
-
允许自问自答吗?
-
@ClarkKent 欢迎来到 StackOverflow。 it is OK to answer your own question 时,StackOverflow 格式要求问题包含问题,并且将答案放在答案中。每个都单独投票。这种格式是 StackOverflow 如此成功的关键原因。请尊重它。
标签: arrays linux bash shell for-loop