【问题标题】:Bash: loop through variables containing pattern in nameBash:遍历名称中包含模式的变量
【发布时间】:2014-11-04 02:15:46
【问题描述】:

在我的脚本中,我有未知数量的包含角度的变量,我想将它们转换为向量。我制作了变量名称,使得每个“参数”的角度都具有以下形式: {参数}_angle_{纬度/perp} 因此,每个参数都有一个“lat”和“perp”角度变量。 所以我想要做的是找到所有包含“_angle_lat”的变量,对这些变量的值进行一些计算,并创建一个包含“参数”名称的新变量。 例如:

export M0_angle_lat=4
export M1_angle_lat=70
export M1_angle_perp=8
export M0_angle_perp=90

# Now I want to use these values to calculate vectors
for varname in *_angle_lat
do
    # at first iteration it will get for example "varname=M0_angle_lat" in which case
    # I want it to do:
    M0_X=$(( $M0_angle_lat * $M0_angle_perp ))
    # The second iteration in case would then give "varname=M1_angle_lat" in which case
    # I want it to do:
    M1_X=$(( $M1_angle_lat * $M1_angle_perp ))
done

我希望我的目标很清楚。感谢您的帮助!

【问题讨论】:

  • bash 真的不是这种编程语言。它确实有数组,但它们不适合构建更复杂的数据结构,并且尝试使用动态生成的变量名称编写代码充其量是容易出错的。 (虽然bash 4.3 引入了一个让它变得更简单的特性。)
  • 我使用bash的原因是因为它是一个小脚本,必须在集群上调用,并且所有周围的代码和脚本也在bash中。
  • 如果您可以将变量重写为可通过前缀匹配,那么您可以使用${!prefix*}${!prefix@} 扩展来获取以prefix 开头的变量的名称。
  • 这也是@chepner 在他的回答中所建议的。但是将“参数”名称作为前缀的原因是由于该参数的一些其他属性也必须定义并且应该分组。尽管接受的答案可以解决我的问题而无需更改前缀。但是感谢您的建议!

标签: bash variables for-loop


【解决方案1】:

您可以使用env 获取所有变量的列表,然后遍历它们:

while IFS='=' read -r name value ; do
  if [[ $name == *'_angle_lat'* ]]; then
    echo "$name" ${!name}
    prefix=${name%%_*} # delete longest match from back (everything after first _)
    angle_lat="${prefix}_angle_lat"
    angle_perp="${prefix}_angle_perp"
    result="${prefix}_X"
    declare "${result}=$(( ${!angle_lat} * ${!angle_perp} ))"       
  fi
done < <(env)

【讨论】:

  • 这似乎是一个巨大的进步。但在这种情况下,$var 是整个字符串:M0_angle_lat=4。如何从中提取“参数”(在这种情况下为M0)?以及如何使用它来创建变量M0_X=$(( $M0_angle_lat * $M0_angle_perp ))
  • 我看到了这段代码试图做什么,但它似乎不起作用。我刚刚检查了集群上运行的 bash 版本,结果发现它是旧版本:GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu) Copyright (C) 2005 Free Software Foundation, Inc.
  • 原来我之前的bash没有问题,只是代码中的一些小错误,现在已经修复了!
【解决方案2】:

此代码需要bash 4.3,才能使用declare -n 创建的命名引用。

这也需要您稍微重命名变量。

angle_lat_M0=4
angle_lat_M1=70
angle_perp_M1=8
angle_perp_M0=90

# Now I want to use these values to calculate vectors
for varname in ${!angle_lat*}
do
    # Ref to angle_lat_*
    declare -n lat=$varname
    # Ref to angle_perp_*
    declare -n perp=${varname/_lat_/_perp_}
    # Ref to X_*
    declare -n x=${varname/angle_lat_/X_}

    x=$((lat * perp))
done

echo $X_M0
echo $X_M1

在 4.3 之前,您需要一些额外的技巧才能工作varname。 (其实并没有我想象的那么糟糕。)

angle_lat_M0=4
angle_lat_M1=70
angle_perp_M1=8
angle_perp_M0=90

# Now I want to use these values to calculate vectors
for varname in ${!angle_lat*}
do
    tag=${varname#angle_lat_}  # M0, M1, etc
    lat=${!varname}
    perp_name=angle_perp_$tag
    perp=${!perp_name}
    x=$((lat * perp))

    declare "X_$tag=$x"
done

echo $X_M0
echo $X_M1

更简单,它应该适用于所有版本的bash(可能不包括 一些非常旧的版本,但至少支持 3.2)。它更简单主要是因为 它放弃了尝试迭代一组相似的变量名。

lats=( $M0_angle_lat $M1_angle_lat )
perps=( $M0_angle_perp $M1_angle_lat )
declare -a x

for i in "${!lats[@]}"; do
    x+=(${lats[i]} * ${perps[i]})
done

M0_X=${x[0]}
M1_X=${x[1]}
# or
for i in "${!x[@]}"; do
    declare "M${i}_X"=${x[i]}
done

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-24
    • 2012-11-09
    • 2022-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    相关资源
    最近更新 更多