4

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

我正在尝试编写一个脚本来比较两个具有相似列的 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
4

1 回答 1

14
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.

@shelltereval在自已删除的评论中提到的基于 - 的解决方案不仅出于安全原因(正如他们提到的那样),而且因为它在引用方面可能会变得非常棘手;为了完整起见,这是基于 - 的解决方案: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. 由于这个问题是关于数组变量的,因此不再进一步讨论这种方法。

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

  • [最简单,但可能会中断]:使用declare

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

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

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