【问题标题】:Assign to a bash array variable indirectly, by dynamically constructed variable name通过动态构造的变量名间接分配给 bash 数组变量
【发布时间】:2014-07-12 06:13:07
【问题描述】:

Bash 脚本从 csv 创建具有未知列的多个数组。

我正在尝试编写一个脚本来比较两个具有相似列的 csv 文件。我需要它从其他 csv 中找到匹配列并比较任何差异。踢球者是我希望脚本是动态的,以允许输入任意数量的列并且它仍然能够运行。我以为我有一个很好的计划来解决这个问题,但结果我遇到了语法错误。这是我需要比较的 csv 样本。

IP address, Notes,  Nmap-SSH,   Nmap-SMTP, Nmap-HTTP, Nmap-HTTPS,
10.0.0.1,   ,       open,       closed,     open,     open,
10.0.0.2,   ,       closed,     open,       closed,   closed,

当我阅读 csv 文件时,我计划查找“IF 列 == 打开;然后;使用 IP 地址填充此列的数组”在这种情况下,这将给我 4 个列表,其中包含正在侦听的 IP说港口。然后,我可以将其与我的安全设备配置进行比较,以确保其配置正确。最后是肉,这是我认为可以完成创建数组供我稍后搜索的内容。但是,当我尝试在数组名称中使用变量时遇到了障碍。可以纠正我的语法还是有更好的方法来做这种事情?

#!/bin/bash
#
#
# This script compares config_cleaned_<ip>.txt output against ext_web_env.csv and outputs the differences
#
#
# Read from ext_web_env.csv file and create Array
#
        FILENAME=./tmp/ext_web_env.csv
#
        index=0
#
        while read line
          do
# How many columns are in the .csv?
        varEnvCol=$(echo $line | awk -F, '{print NF}')
            echo "columns = $varEnvCol"

# While loop to create array for each column

                while [ $varEnvCol != 2 ]
                  do
# Checks to see if port is open; if so then add IP address to array
                   varPortCon=$(echo $line | awk -F, -v i=$varEnvCol '{print $i}')
                        if [ $varPortCon = "open" ]
                          then
                                arr$varEnvCol[$index]="$(echo $line | awk -F, '{print $1}')"
# I get this error message "line29 : arr8[194]=10.0.0.194: command not found"
                        fi
           echo "arrEnv$varEnvCol is: ${arr$varEnvCol[@]}"
# Another error but not as important since I am using this to debug "line31: arr$varEnvCol is: ${arr$varEnvCol[@]}: bad substitution"
                   varEnvCol=$(($varEnvCol - 1))
                done
                index=$(($index + 1 ))
          done < $FILENAME

更新

我也尝试使用 eval 命令,因为所有数据都将由其他脚本填充。

但我收到此错误消息:

./compare.sh:第 41 行:arr8[83]=10.0.0.83:找不到命令

这是我在这个例子中的新代码:

 if [[ $varPortCon = *'open'* ]]
   then
    eval arr\$varEnvCol[$index]=$(echo $line | awk -F, '{print $1}')
 fi

【问题讨论】:

  • 您已经在脚本中使用了 awk,为什么不直接使用它呢?
  • @technosaurus 有没有办法使用 awk 来完成我想做的事情?

标签: arrays bash variables eval indirection


【解决方案1】:
arr$varEnvCol[$index]="$(...)"

不会按您期望的方式工作 - 您不能间接地 分配给 shell 变量 - 通过 扩展 到变量名 - 这样

您尝试使用 eval 的解决方法也存在缺陷 - 见下文。


tl;博士

如果你使用 bash 4.3 或更高版本:

declare -n targetArray="arr$varEnvCol"
targetArray[index]=$(echo $line | awk -F, '{print $1}')

bash 4.2 或更早版本:

declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')"

警告:这将适用于您的特定情况,但可能会在其他情况下巧妙地失败;继续阅读以了解详细信息,包括基于read更强大但更繁琐的替代方案。

@shellter 在自已删除的评论中提到的基于eval 的解决方案不仅出于安全原因(正如他们所提到的那样)存在问题,而且还因为在引用方面可能会变得非常棘手;为了完整起见,这是基于eval 的解决方案:

eval "arr$varEnvCol[index]"='$(echo $line | awk -F, '\''{print $1}'\'')'

请参阅下面的说明。


分配给bash数组变量间接

bash 4.3+:使用declare -n 有效地创建另一个变量的别名 ('nameref')

这是迄今为止最好的选择(如果有的话):

declare -n targetArray="arr$varEnvCol"
targetArray[index]=$(echo $line | awk -F, '{print $1}')

declare -n 有效地允许您通过另一个名称引用变量(无论该变量是否为数组),并且为其创建别名的名称可以是表达式的结果(一个扩展的字符串),如图所示。

bash 4.2-:有几个选项,每个选项都有权衡

注意:对于非数组变量,最好的方法是使用printf -v。由于这个问题是关于 array 变量的,所以不进一步讨论这种方法。

  • [最强大,但很麻烦]:使用read
IFS=$'\n' read -r -d '' "arr$varEnvCol"[index] <<<"$(echo $line | awk -F, '{print $1}')"
  • IFS=$'\n' 确保每个输入行中的前导和尾随空格保持不变。
  • -r 阻止对 \ 字符的解释。在输入中。
  • -d '' 确保捕获所有输入,甚至是多行输入
    • 但是,请注意,任何 尾随 \n 字符。被剥离
    • 如果您只对输入的第一行感兴趣,请省略-d ''
  • "arr$varEnvCol"[index] 扩展为变量 - 数组元素,在本例中 - to assign to;请注意,在数组下标中引用变量index 不需要$ 前缀,因为下标是在算术 上下文中计算的,其中前缀是可选
  • &lt;&lt;&lt; - 所谓的here-string - 将其参数发送到stdinread 从中获取输入。

  • [最简单,但可能会崩溃]:使用declare

declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')"
  • (这有点违反直觉,因为declare 旨在声明,而不是修改变量,但它适用于 bash 3.x 和 4 .x,具有如下所述的约束。)
  • 在函数外工作正常 - 数组是否使用 declare 显式声明。
  • 警告:在函数内部,仅适用于 LOCAL 变量 - 您不能从以这种方式在函数内部。尝试这样做总是会创建一个 LOCAL 变量 ECLIPSING shell 全局变量。

  • [不安全且棘手]:使用eval

eval "arr$varEnvCol[index]"='$(echo $line | awk -F, '\''{print $1}'\'')'
  • 警告:如果您完全控制正在评估的字符串的内容,请仅使用evaleval 将执行字符串中包含的任何命令,可能会产生不想要的结果。
  • 了解哪些变量引用/命令替换在重要时会被扩展 - 最安全的方法是延迟扩展,这样它们就会在 eval 执行而不是 立即参数被传递给eval时发生的扩展。
  • 要使变量赋值语句成功,RHS(右侧)最终必须评估为 单个 标记 - 不带引号的 没有 空格或 带引号(可选用空格)。
  • 上面的例子使用引号来延迟扩展;因此,传递的字符串不能直接包含单引号,因此被分成多个部分,文字为' 字符。 拼接\'
  • 另请注意,传递给eval 的赋值语句的 LHS(左侧)必须是 双引号 字符串 - 使用带有选择性引用 $ 的不带引号的字符串行不通,奇怪的是:
    • 好的:eval "arr$varEnvCol[index]"=...
    • 失败:eval arr\$varEnvCol[index]=...

【讨论】:

    猜你喜欢
    • 2016-12-18
    • 2018-07-22
    • 2017-11-06
    • 2020-08-14
    • 1970-01-01
    • 1970-01-01
    • 2020-01-20
    • 2020-09-14
    相关资源
    最近更新 更多