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

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

问题描述

Bash脚本从未知列的csv创建多个磁盘阵列。

我试图写一个脚本两个CSV文件的比较相似列。我需要它来定位从另一个CSV匹配的列和比较的任何差异。踢球者是我想的脚本是动态的,以允许输入的任何数量的列,它仍然能够正常工作。我想我有一个很好的计划,以解决这个问题,但事实证明我运行到语法错误。下面是一个CSV我需要比较的样本。

  IP地址,注释的Nmap-SSH,Nmap的-SMTP,Nmap的-HTTP,Nmap的-HTTPS,
10.0.0.1,开放式,封闭式,开放式,开放式,
10.0.0.2,封闭,开放,封闭,全封闭,

当我读了CSV文件,我准备寻找IF列==打开;然后,填充此列的IP地址为阵这给了我4列出了该方案与被监听的IP地址说端口。然后,我可以比较,为我的安全设备配置,以确保它被正确配置。终于到了肉,这里是我认为将完成创建阵列为我以后搜索。不过,我遇到了一个障碍,当我试图使用数组名里的一个变量。可我的语法被纠正或有只是一个更好的方式做这样的事情?

 #!/斌/庆典


#这个脚本比较config_cleaned_<知识产权及GT;反对ext_web_env.csv的.txt输出,并且输出的差异


#从ext_web_env.csv文件中读取并创建阵列

        FILENAME =的/ tmp / ext_web_env.csv

        指数= 0

        而读线
          做
#有多少列在.csv?
        varEnvCol = $(回声$线| awk的-F,'{print NF将}')
            回声栏目= $ varEnvCol#While循环对每个列创建数组                而[$ varEnvCol!= 2]
                  做
#检查是否端口是开放的;如果是的话IP地址阵列添加
                   varPortCon = $(回声$线| awk的-F,-v I = $ varEnvCol'{$打印I}')
                        如果[$ varPortCon =开放]
                          然后
                                ARR $ varEnvCol [$指数] =$(回声$线| awk的-F,'{$打印1})
#我得到这个错误消息line29:arr8 [194] = 10.0.0.194:命令未找到
                        科幻
           回声arrEnv $ varEnvCol是:$ {$ ARR varEnvCol [@]}
#另一个错误,但不是重要的,因为我使用这个调试line31:ARR $ varEnvCol是:$ {$ ARR varEnvCol [@]}:坏替代
                   varEnvCol = $(($ varEnvCol - 1))
                DONE
                指数= $(($指数+ 1))
          完成< $ FILENAME

更新

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

但我收到此错误信息:

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

下面是我的新code这个例子:

 如果[[$ varPortCon = *'开放'*]
   然后
    EVAL ARR \\ $ varEnvCol [$指数] = $(回声$线| awk的-F,'{$打印1})
 科幻


解决方案

改编$ varEnvCol [$指数] =$( ...)

不工作,你指望它的方式 - 你不能分配给shell变量间接的 - 即通过前pression扩展的变量名 - 这样

您尝试使用评估解决方法也有缺陷 - 见下文


TL;博士

如果你使用bash 4.3以上:

 声明-n targetArray =改编$ varEnvCol
targetArray [指数] = $(回声$线| awk的-F,'{$打印1})

的bash 4.2或更早版本:

 宣布改编$ varEnvCol[指数] =$(回声$线| awk的-F,'{$打印1})

买者的:这将在特定情况下工作,但的可以在别人失败的巧妙的;阅读详细信息,其中包括的基于更强劲,但繁琐的替代

@shellter 中提到的评估基于解决方案一个自删除的评论是有问题的不仅是出于安全原因(如他们所说),而且还因为它可以得到相对的引用的相当棘手;为了完整性,这里的评估基于解决方案:

 评估改编$ varEnvCol [指数]= $(回声$线| awk的-F,'\\''{$打印1}'\\'')'

请参阅下面的说明。


分配到庆典数组变量的间接

bash的4.3 + :使用声明-n 来有效地创建一个别名的(另一个变量

的'nameref')

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

 声明-n targetArray =改编$ varEnvCol
targetArray [指数] = $(回声$线| awk的-F,'{$打印1})

声明-n 有效地让您为的另一个名字引用变量(无论变量是一个数组或没有),以及名创建一个别名可能是一个前pression(扩大字符串)的结果,这表现。

的bash 4.2 - :还有的若干选项的,每一个的权衡

注:使用非数组的变量,最好的办法是为使用的printf -v 。由于这个问题是关于阵列的变量,这种做法是不进一步讨论。


  • [最强大的,但繁琐]:使用

IFS = $'\\ n'读-r -d''改编$ varEnvCol[指数]<&LT ;<$(回声$线| awk的-F,'{$打印1})


  • IFS = $'\\ n'确保在每个输入行的开头和结尾的空格保持不变。

  • -r prevents间pretation \\ 字符。输入。

  • -d''确保所有输入被捕获,甚至多行即可。

    • 但是请注意,任何的 \\ n 字符。被剥离

    • 如果你的只关心在第一个的输入线,忽略 -d''


  • 改编$ varEnvCol[指数] 扩展到变量 - 数组元素,在这种情况下 - 以分配的到;注意,引用变量首页里的数组下标并不需要 $ preFIX,因为标进行评价在算术的背景下,其中preFIX是的可选

  • <<< - 所谓的下面的字符串的 - 将其参数标准输入,其中从需要它的输入。


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


宣布改编$ varEnvCol[指数] =$(回声$线| awk的-F,'{打印$ 1 }')


  • (这是稍有反直觉的,因为声明是为了的声明的,不是的修改的变量,但它工作在bash 3.x和4.x,与限制如下所述。)

  • 做工精细外的函数 - 无论是阵列已明确宣布声明与否

  • 买者的:里面的功能,只适用于局部变量 - 您不能引用从壳全局变量(声明的函数之外的变量)一个函数内部这样。试图这样做总是创建一个局部变量食壳全局变量。


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


评估改编$ varEnvCol [指数]= $(回声$线| awk的-F,'\\'' {打印$ 1}'\\'')'


  • 警告:仅使用评估如果你完全控制字符串被评价的内容; 评估将执行的任何的字符串中包含的,具有潜在的不想要的结果的命令。

  • 了解得到扩展哪些变量引用/命令替换的时候是平凡的 - 在安全的做法是的延迟的扩展让他们发生时,评估执行,而不是的直接的扩展当这种情况发生的参数传递给评估

  • 对于一个变量赋值语句成功,RHS(右手边)最终必须评估到的的象征 - 无论是加引号的没有的空白或报价的(可以选择空格)。

  • 上面的例子使用的的报价延迟扩展;因此,必须通过不直接包含单引号,因此该字符串的分解成多个部分的字面与 字符。的拼接的如 \\

  • 另外请注意,传递给评估赋值语句的LHS(左侧)必须是一个的双引号的字符串 - 使用一个不带引号的字符串选择性引用的 $ 将无法正常工作,奇怪的是:

    • 确定:评估改编$ varEnvCol [指数]= ...

    • 失败:评估改编\\ $ varEnvCol [指数] = ...


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

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,

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

UPDATE

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

but am getting this error message:

./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]="$(...)"

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.

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


tl;dr

If you use bash 4.3 or above:

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

bash 4.2 or earlier:

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

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.

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}'\'')'

See below for an explanation.


Assign to a bash array variable indirectly:

bash 4.3+: use declare -n to effectively create an alias ('nameref') of another variable

This is by far the best option, if available:

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

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.

bash 4.2-: there are several options, each with tradeoffs

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.

  • [most robust, but cumbersome]: use read:

IFS=$'\n' read -r -d '' "arr$varEnvCol"[index] <<<"$(echo $line | awk -F, '{print $1}')"

  • 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 ''
  • "arr$varEnvCol"[index] expands to the variable - array element, in this case - to assign to; note that referring to variable index inside an array subscript does NOT need the $ prefix, because subscripts are evaluated in arithmetic context, where the prefix is optional.
  • <<< - a so-called here-string - sends its argument to stdin, where read takes its input from.

  • [simplest, but may break]: use declare:

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

  • (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.

  • [insecure and tricky]: use eval:

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

  • 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天全站免登陆