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:line 41:arr8 [83] = 10.0.0.83:找不到命令
以下是此示例的新代码:
if [[ $varPortCon = *'open'* ]]
then
eval arr\$varEnvCol[$index]=$(echo $line | awk -F, '{print $1}')
fi
答案 0 :(得分:13)
arr$varEnvCol[$index]="$(...)"
无法按照您期望的方式工作 - 您无法通过扩展的表达式间接地分配到shell变量变量名 - 这样。
您尝试使用eval
的解决方法也存在缺陷 - 请参阅下文。
declare -n targetArray="arr$varEnvCol"
targetArray[index]=$(echo $line | awk -F, '{print $1}')
declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')"
警告:这将适用于您的特定情况,但可能会在其他情况下巧妙地失败;请继续阅读详细信息,包括基于 read
的更强大但更繁琐的替代方案。
@shellter在自已删除的评论中提到的基于eval
的解决方案不仅出于安全原因(如他们所提到的)而存在问题,而且因为它可能会非常棘手< EM>引用;为了完整性,这是基于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]
扩展为变量 - 数组元素,在本例中为 assign to;请注意,在数组下标中引用变量index
不需要$
前缀,因为下标是在算术上下文中计算的,其中前缀为可选 <<<
- 一个所谓的 here-string - 将其参数发送到stdin
,其中read
从中获取输入。
[最简单,但可能会中断]:使用declare
:
declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')"
declare
意味着声明,而不是修改变量,但它适用于bash 3.x和4.x,具有下面提到的约束。)declare
显式声明。警告:INSIDE一个函数,只适用于LOCAL变量 - 你不能引用shell-global变量(在函数外声明的变量)在函数内部。尝试这样做总是会创建一个LOCAL变量ECLIPSING shell-global变量。
[不安全和棘手]:使用eval
:
eval "arr$varEnvCol[index]"='$(echo $line | awk -F, '\''{print $1}'\'')'
eval
; eval
将执行包含在字符串中的任何命令,但可能会产生不需要的结果。 eval
执行时发生而不是参数传递给 eval
时发生的立即扩展。'
个字符。 拼接在中\'
。eval
的赋值语句的LHS(左侧)必须是双引号字符串 - 使用不带引号的字符串,并选择性引用{{奇怪的是,1}}无法运作:
$
eval "arr$varEnvCol[index]"=...