评估if语句中的变量

时间:2014-05-21 12:33:58

标签: bash variables eval expansion

所以我有一个像:

这样的数组
al_ap_version=('ap_version' '[[ $data -ne $version ]]')

条件在循环中得到评估,如:

for alert in alert_list; do
    data=$(tail -1 somefile)
    condition=$(eval echo \${$alert[1]})
    if eval "$condition" ; then
        echo SomeAlert
    fi
done

虽然这通常适用于许多场景,但如果$ data返回类似“ - / - ”或“4.2.9”的内容,我会收到错误,因为它似乎不喜欢变量中的复杂字符串。

显然我不能将变量括在单引号中,因为它不会扩展所以我想要扩展$ data变量(或者实际上是版本的var,它会遭受同样的可能命运)评估可以处理?

2 个答案:

答案 0 :(得分:1)

忽略eval在这里使用可能非常危险的事实(除非somefile中的数据由您控制且仅由您控制),您的示例代码中有一些问题需要解决。

for循环中,alert_list必须为$alert_list

另外,正如@choroba所指出的那样,您应该使用!=而不是-ne,因为您的输入并不总是整数。

最后,在调试时,您可以将set -x添加到脚本的顶部,或将-x添加到shebang行的末尾以启用详细输出(有助于确定bash如何扩展您的变量)。

这对我有用:

#!/bin/bash -x

data=2.2
version=1

al_ap_version=('ap_version' '[[ $data != $version ]]')
alert_list='al_ap_version'

for alert in $alert_list; do
    condition=$(eval echo \${$alert[1]})
    if eval "$condition"; then
        echo "alert"
    fi
done

答案 1 :(得分:1)

你可以尝试一种更实用的方法,即使bash只是勉强能够做到这一点。总的来说,将要执行的动作打包到bash函数并使用函数名称引用它通常要容易得多,而不是试图将动作保持为要评估的字符串。

但首先,使用数组名称数组很尴尬。让我们摆脱它。

我不清楚数组ap_version中元素0 al_ap_version}的要点,但我认为它与错误消息有关。如果警报处理的顺序不重要,您可以用单个关联数组替换数组名称列表:

declare -A alert_list
alert_list[ap_version]=... # see below
alert_list[os_dsk]=...

然后用:

处理它们
for alert_name in ${!alert_list[@]}; do
  alert=${alert_list[$alert_name]}
  ...
done

这样做之后,我们可以通过为每个警报创建一个bash函数来摆脱eval,因此对于杂耍引号具有丑陋的必要性:

check_ap_version() {
  (($version != $1))
}

修改:似乎$1不一定是数字,因此最好使用非数字比较,但确切的版本匹配可能不是您的'在任何一个之后。所以也许最好使用:

check_ap_version() {
  [[ $version != $1 ]]
}

注意函数的第一个参数是数据值的约定。

现在我们可以将函数的名称插入alert数组,并在循环中间接调用它:

declare -A alert_list
alert_list[ap_version]=check_ap_version
alert_list[os_dsk]=check_op_dsk

check_alerts() {
  local alert_name alert
  local data=$(tail -1 somefile)
  for alert_name in ${!alert_list[@]}; do
    alert=${alert_list[$alert_name]}
    if $alert "$data"; then
      signal_alert $alert_name
    fi
  done
}

如果您准备对函数名称更加严格,则可以避免关联数组,从而按顺序处理警报。例如,假设每个函数都具有名称check_<alert_name>。然后上面可能是:

alert_list=(ap_version os_dsk)

check_alerts() {
  local alert_name
  local data=$(tail -1 somefile)
  for alert_name in $alert_list[@]; do
    if check_$alert_name "$data"; then
      signal_alert $alert_name
    fi
  done
}