我有包含 IP 地址和开放端口的 CSV 文件:
IP,1,3,4,6,7,9,13,17,19,20,21,22,23,24,25,26
1.1.1.2,,,,,,,,,,,open,,,,,
1.1.1.3,,,,,,,,,,,open,,,,,
1.1.1.4,,open ,open,,,,,,,,open,,,,,
1.1.1.5,,,,,,,,,,,open,,,,,
1.2.3.4,,,,,,,,,,,open,,,,,
1.4.5.6,,,,,open,,,,,,open,,,,,
1.4.5.6,,,,,,,,,,,open,,,,,
1.1.3.4,,,,,,,,,,,open,,,,,
对于每个具有开放端口的 IP 地址,我需要使用 IP 地址和开放端口的端口号(取自 CSV 标头)执行命令。
答案1
解决这个问题完全地我认为纯粹的bash
做法是不可取的。参见例如问题“为什么使用 shell 循环处理文本被认为是不好的做法?”。
相反,让我们让输入数据更容易理解。
awk 'FNR == 1 { split($0, port, ","); FS=","; next }
{ for (i=2; i<=NF; ++i) if ($i == "open") print $1, port[i] }' file.csv
此awk
命令首先将 CSV 文件第一行中的端口号读取到名为 的数组中port
。列的端口号N
将存储在port[N]
.
它通过将输入的第一行用逗号分隔并将结果存储在数组中来实现这一点port
。这就是split()
命令的作用。条件FNR == 1
表示“如果这是文件的第一行,则执行以下操作...”(FNR
是当前输入文件中的行号)。对于每一行输入,都会执行一个没有条件的代码块。
调用split()
,后FS
,字段分隔符设置为逗号。这意味着文件中的其他行将自动以逗号分隔为字段。这在第二个块的循环中使用,以循环每行上的 CSV 字段(从第二个字段到最后一个字段)。
对于数据中的其他每一行,它循环遍历以逗号分隔的字段,当找到值为 string 的字段时open
,它会打印出 IP 地址(第一个字段)和相应的端口号。
考虑到问题中的数据,该命令的输出是
1.1.1.2 21
1.1.1.3 21
1.1.1.4 4
1.1.1.4 21
1.1.1.5 21
1.2.3.4 21
1.4.5.6 7
1.4.5.6 21
1.4.5.6 21
1.1.3.4 21
这可以通过 shell 中的循环轻松读取:
while read -r ip port; do
telnet "$ip" "$port" # or whatever your command is
done
这将一一读取 IP 地址和端口号。
要将它们组合成一个完整的脚本:
#!/bin/sh
awk 'FNR == 1 { split($0, port, ","); FS=","; next }
{ for (i=2; i<=NF; ++i) if ($i == "open") print $1, port[i] }' file.csv |
while read -r ip port; do
telnet "$ip" "$port" # or whatever your command is
done
命令的输出awk
通过管道传送到while
循环,该循环读取值并调用命令(注意|
输入文件名后面的管道)。
请注意,如果某个 IP 地址打开了多个端口,则该命令将针对该地址执行多次。
答案2
使用 选择您的第一个和下一个相应字段awk
。
awk -d "," -F '{print $1, $n...}