【问题标题】:Nested for loop behavior - shell script嵌套 for 循环行为 - shell 脚本
【发布时间】:2016-06-02 23:25:11
【问题描述】:

有两个 shell 脚本数组。出于演示目的,第一个数组包含两个元素。这两个元素也将在第二个数组中添加另一个数组。

这个嵌套 for 循环的目的是从第二个数组中删除匹配的字符串。因此,最后第三个元素应该是唯一仍在第二个数组中的元素。

我相信:

  • 我的 for 循环语法/算法正在跳过迭代
  • 我对unset数组方法的理解不正确

请注意,我想完全删除元素,而不仅仅是将元素留空。

代码

first_string='first'
second_string='second'
third_string='third'

strings_to_remove=()
strings_to_remove+=("$first_string")
strings_to_remove+=("$second_string")

main_array=()
main_array+=("$first_string")
main_array+=("$second_string")
main_array=("$third_string")

for i in "${main_array[@]}";    do
    echo $i
done

echo ''

for r in "${!strings_to_remove[@]}"; do
  index=''
  for i in "${!main_array[@]}"; do
    if [[ "${main_array[$i]}" = "${strings_to_remove[$r]}" ]];  then
      index=$i
    fi
  done
  if [[ $index -ne '' ]]; then
    unset main_array[$index]
    main_array=( "${main_array[@]}" )
  fi
done

echo ''

for i in "${main_array[@]}";    do
    echo "$i"
done

输出

first
second
third

first
third

应该删除第一个和第二个元素,但是只删除第二个元素。我不确定算法本身或语法是否不正确。

【问题讨论】:

标签: arrays shell iteration nested-loops unset


【解决方案1】:

问题出在这里:

[[ $index -ne '' ]]

这是一个算术测试,[[ 0 -ne '' ]] 返回false。更改自:

index=''
...
if [[ $index -ne '' ]]; then

到:

index=-1
...
if [[ $index -ge 0 ]]; then

你就完成了。

【讨论】:

  • 您对这个问题是正确的,它需要解决。但是我认为主要问题是 $index 的分配在 for 循环中被覆盖。有 2 个匹配的字符串。由于找到第一个匹配项时循环不会退出,因此它将始终被第二个分配覆盖。这就是为什么第二个元素被删除而第一个元素不被删除的原因。
  • @MattClendenen $index 在内部 for 循环之前被清理,该循环在数组中搜索值,正如预期的那样。由于-ne '',第一个元素没有被删除。尝试使用bash -x the_script 运行您的脚本。
  • 您的解决方案是正确的。最初我被 $index 的覆盖挂断了,这确实发生了。但是,在外循环的最后一次迭代中。只有一个匹配而不是两个,因此它不会覆盖并删除剩余的元素。使用此算法,将首先删除数组中的最后一个匹配项。
【解决方案2】:

我会这样做:

strings_to_remove=( first second )
main_array=( "${strings_to_remove[@]}" third )
unique=()

stringified_remove=$( IFS=:; echo "${strings_to_remove[*]}" )
for elem in "${main_array[@]}"; do 
    if [[ ":$stringified_remove:" != *:"$elem":* ]]; then
        unique+=( "$elem" )
    fi
done

declare -p unique
declare -a unique='([0]="third")'

在 bash 中,[[ double brackets ]]==!=模式匹配 运算符。所以我们将模式*:first:* 与数组:first:second: 的字符串形式进行比较。大多数时候,这已经足够了。如果您的删除数组包含“foo:bar”之类的内容并且您的保留数组包含“foo”,或者通常,如果您的数据中出现“加入”字符(我使用了冒号),它可能会给您带来误报。

如果您需要更明确的“包含”检查,则必须这样做:

for check in "${main_array[@]}"; do
    add=true
    for remove in "${strings_to_remove[@]}"; do              
        if [[ $check == $remove ]]; then         
            add=false
            break
        fi
    done
    $add && unique+=("$check")
done

【讨论】:

    猜你喜欢
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-05-17
    • 2019-04-04
    • 2013-08-09
    • 2013-06-05
    相关资源
    最近更新 更多