在Bash中取消设置元素

时间:2016-02-25 02:26:10

标签: bash unix

我正在尝试遍历bash数组,如果它与正则表达式不匹配则取消设置值。代码块不会改变数组,任何想法?

    for ((q=0; q<{#array[*]}; q++));
    do
        if [[  ${array[q]} =~ .*\.c$ ]]; then
            :
        else unset array[q]
    done

4 个答案:

答案 0 :(得分:1)

您错过了fi语句的结束if。但是,模式匹配在这里稍微简单一点,并且允许您使用!=运算符来避免使用else子句。

for ((q=0; q<${#array[*]}; q++));
do
    if [[  ${array[q]} != *.c ]]; then
        unset array[q]
    fi
done

答案 1 :(得分:1)

您的问题是您继续在循环条件中评估${#array[*]},而不是先确定一次

这样做,你最终会丢失循环中的数组元素 [1]

请改为尝试:

#!/usr/bin/env bash

array=( foo.c non bar.c other ) # sample input

count=${#array[*]} # determine array size *beforehand*
for (( q=0; q < count; q++)); do
  if [[ ${array[q]} =~ \.c$ ]]; then # note: no need for .* before \.c$
      :
  else 
      unset array[q]
  fi
done

printf '%s\n' "${array[@]}" # prints 'foo.c' and 'bar.c'

dancancode's answer显示了一种更简洁的方法来预先分配数组元素:通过将其合并到循环条件的第一个语句
for (( q=0, count=${#array[@]}; q < count; ++q ))
但是,count仍在 script 范围内隐式声明(就像q),即作用于for循环

请注意,虽然取消设置单个数组元素正确地反映在元素计数{#array[*]}中,但索引更改,而最终会导致稀疏< / em> array。

因此,当您稍后枚举数组时,请使用:

,而不是循环遍历顺序索引
  • for element in "${array[@]}"; ... - 直接元素枚举
  • 或:for i in ${!array[@]}; do element=${array[i]}; ... - 通过 list 进行枚举 - 可能是非顺序的索引${!array[@]})。

或者,您可以根据自身的副本重新定义稀疏数组将其转换为非稀疏数组(从0开始的连续索引)。

array=( "${array[@]}" ) # convert to sequential indices starting at 0 

[1] 问题的证明:

array=( one two three four )
for (( i=0; i < ${#array[@]}; i++)); do
  echo "element: ${array[i]}; element count: ${#array[@]}"
  unset array[i]
done

产量

element: one; element count: 4
element: two; element count: 3

换句话说:只处理了4个元素中的前2个,因为每次迭代中的数组计数(${#array[@]})减少(由于未设置的元素)导致提前终止循环。

注意暗示:${#array[@]}始终反映元素 count ,无论数组是否稀疏(具有非顺序索引)。

答案 2 :(得分:0)

$array=('1' '2' '3' '4' 'abcd' '5')
$echo "${array[@]}"
1 2 3 4 abcd 5
$cnt=${#array[@]}
$for ((q=0;q<cnt;q++))
do
    if [[ ${array[q]} =~ ^[0-9]$ ]]
    then
        :
    else
        unset array[q]
    fi
done
$ echo "${array[@]}"
1 2 3 4 5

您可以使用的另一种简写就是这样

cnt=${#array[@]}
for ((q=0;q<cnt;q++))
do
    [[ ${array[q]} =~ ^[0-9]$ ]] || unset array[q]
done

您在fi中也遗漏了if,语法错误

cnt=${#array[@]}
for ((q=0; q<cnt; q++));
do
    if [[  ${array[q]} =~ .*\.c$ ]]; then
        :
    else 
         unset array[q]
    fi
done

速记

cnt=${#array[@]}
for ((q=0; q<cnt; q++));
do
    [[  ${array[q]} =~ .*\.c$ ]] || unset array[q]
done

答案 3 :(得分:0)

这是一个更大的脚本的一部分:

#!/bin/bash

shopt -s extglob nullglob

prune () { eval "for ((i=0, j=\${#$1[@]}; i<j; i++)); do [[ \${$1[i]} == $2 ]] && unset '$1[i]'; done; $1=(\"\${$1[@]}\")"; }

rpms=(*.rpm)
[[ $rpms ]] || exit 0

prune rpms '@(MQSeries|grafana|influxdb)*'

unset实际上有效,但它在数组中留下了漏洞。如果重新定义数组以包含自身,则孔将消失。