通过动态构造的变量名间接分配给 bash 数组变量 [英] Assign to a bash array variable indirectly, by dynamically constructed variable name

查看:26
本文介绍了通过动态构造的变量名间接分配给 bash 数组变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Bash 脚本,用于从 csv 中创建多个带有未知列的数组.

Bash script to create multiple arrays from csv with unknown columns.

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

I am trying to write a script to compare two csv files with similar columns. I need it to locate the matching column from the other csv and compare any differences. The kicker is I would like the script to be dynamic to allow any number of columns to be entered and it still be able to function. I thought I had a good plan to solve this but turns out I'm running into syntax errors. Here is a sample of a csv I need to compare.

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 column == open; then; populate this column's array with the IP address"这将在这种情况下为我提供 4 个列表,其中包含正在侦听的 IP说港口.然后,我可以将其与我的安全设备配置进行比较,以确保其配置正确.最后是肉,这是我认为可以完成创建数组供我稍后搜索的内容.但是,当我尝试在数组名称中使用变量时遇到了障碍.可以更正我的语法还是有更好的方法来做这种事情?

When I read the csv file I was planning to look for "IF column == open; then; populate this column's array with the IP address" This would have given me 4 lists in this scenario with the IPs that were listening on said port. I could then compare that to my security device configuration to make sure it was configured properly. Finally to the meat, here is what I thought would accomplish creating the arrays for me to search later. However I ran into a snag when I tried to use a variable inside an array name. Can my syntax be corrected or is there just a better way to do this sort of thing?

#!/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 命令,因为所有数据都将由其他脚本填充.

I also tried using the eval command since all the data will be populated by other scripts.

但我收到此错误消息:

./compare.sh: line 41: arr8[83]=10.0.0.83: command not found

./compare.sh: line 41: arr8[83]=10.0.0.83: command not found

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

Here is my new code for this example:

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

推荐答案

arr$varEnvCol[$index]="$(...)"

不像您期望的那样工作 - 您不能间接分配给 shell 变量 - 通过一个扩展到变量名称 - 这种方式.

doesn't work the way you expect it to - you cannot assign to shell variables indirectly - via an expression that expands to the variable name - this way.

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

Your attempted workaround with eval is also flawed - see below.

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更强大但更麻烦的替代方案.

Caveat: This will work in your particular situation, but may fail subtly in others; read on for details, including a more robust, but cumbersome alternative based on read.

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

The eval-based solution mentioned by @shellter in a since-deleted comment is problematic not only for security reasons (as they mentioned), but also because it can get quite tricky with respect to quoting; for completeness, here's the eval-based solution:

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

请参阅下面的说明.

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

This is by far the best option, if available:

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

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

declare -n effectively allows you to refer to a variable by another name (whether that variable is an array or not), and the name to create an alias for can be the result of an expression (an expanded string), as demonstrated.

注意:对于非数组变量,最好的方法是使用printf -v.由于这个问题与数组变量有关,因此不再进一步讨论这种方法.

NOTE: With non-array variables, the best approach is to use printf -v. Since this question is about array variables, this approach is not discussed further.

  • [最健壮,但很麻烦]:使用read:
IFS=$'\n' read -r -d '' "arr$varEnvCol"[index] <<<"$(echo $line | awk -F, '{print $1}')"

  • IFS=$'\n' 确保每个输入行中的前导和尾随空格保持不变.
  • -r 防止解释 \ 字符.在输入中.
  • -d '' 确保捕获所有输入,即使是多行输入.
    • 但是请注意,任何 尾随 \n 字符.被剥离.
    • 如果您只对输入的第一行感兴趣,请省略-d ''
      • IFS=$'\n' ensures that that leading and trailing whitespace in each input line is left intact.
      • -r prevents interpretation of \ chars. in the input.
      • -d '' ensures that ALL input is captured, even multi-line.
        • Note, however, that any trailing \n chars. are stripped.
        • If you're only interested in the first line of input, omit -d ''
        • <<< - 所谓的 here-string - 将其参数发送到 stdin,其中 read 接受它的输入.

          <<< - a so-called here-string - sends its argument to stdin, where read takes its input from.

          [最简单,但可能会出错]:使用declare:

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

          • (这有点违反直觉,因为 declare 意味着 declare,而不是 modify 一个变量,但它在 bash 中有效3.x 和 4.x,约束如下.)
          • 在函数之外工作正常 - 无论数组是否使用 declare 显式声明.
          • 警告:在函数内部,仅适用于本地变量 - 您不能从以下位置引用 shell 全局变量(在函数外声明的变量)以这种方式在函数内部.尝试这样做总是会创建一个本地变量 ECLIPSING shell 全局变量.

            • (This is slightly counter-intuitive, in that declare is meant to declare, not modify a variable, but it works in bash 3.x and 4.x, with the constraints noted below.)
            • Works fine OUTSIDE a FUNCTION - whether the array was explicitly declared with declare or not.
            • Caveat: INSIDE a function, only works with LOCAL variables - you cannot reference shell-global variables (variables declared outside the function) from inside a function that way. Attempting to do so invariably creates a LOCAL variable ECLIPSING the shell-global variable.

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

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

              • CAVEAT:仅当您完全控制要评估的字符串的内容时才使用 evaleval 将执行包含在字符串中的任何命令,可能会产生不需要的结果.
              • 了解哪些变量引用/命令替换在重要时扩展 - 最安全的方法是延迟扩展,以便它们在 eval 时发生执行而不是立即扩展,当参数被传递给eval时发生.
              • 要使变量赋值语句成功,RHS(右侧)最终必须评估为单个标记 - 不带引号的没有空格或引号(可选带空格).
              • 上面的例子使用引号来延迟扩展;因此,传递的字符串不能直接包含单引号,因此被分成多个部分,并带有文字 ' 字符.拼接\'.
              • 另请注意,传递给 eval 的赋值语句的 LHS(左侧)必须是 双引号 字符串 - 使用带有选择性引用的不带引号的字符串奇怪的是 $ 不起作用:
                • 好的:eval "arr$varEnvCol[index]"=...
                • 失败:eval arr\$varEnvCol[index]=...
                  • CAVEAT: Only use eval if you fully control the contents of the string being evaluated; eval will execute any command contained in a string, with potentially unwanted results.
                  • Understanding what variable references/command substitutions get expanded when is nontrivial - the safest approach is to delay expansion so that they happen when eval executes rather than immediate expansion that happens when arguments are passed to eval.
                  • For a variable assignment statement to succeed, the RHS (right-hand side) must eventually evaluate to a single token - either unquoted without whitespace or quoted (optionally with whitespace).
                  • The above example uses single quotes to delay expansion; thus, the string passed mustn't contain single quotes directly and thus is broken into multiple parts with literal ' chars. spliced in as \'.
                  • Also note that the LHS (left-hand side) of the assignment statement passed to eval must be a double-quoted string - using an unquoted string with selective quoting of $ won't work, curiously:
                    • OK: eval "arr$varEnvCol[index]"=...
                    • FAILS: eval arr\$varEnvCol[index]=...
                    • 这篇关于通过动态构造的变量名间接分配给 bash 数组变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆