bash - 为列的重复值写入相同的结果
问题描述
我真的是 bash 的新手。URLs.txt
我在 .txt 文件 ( )中有一个域列表。我还想要一个 .csv 文件,它由,
( myFile.csv
) 分隔的 3 列组成。我的代码读取URLs.txt
(每个域)的每一行,找到它的 IP 地址,然后将它们插入myFile.csv
(第一列中的域,第二列中的 IP。
Name, IP
ex1.com, 10.20.30.40
ex2.com, 20.30.40.30
ex3.com, 10.45.60.20
ex4.com, 10.20.30.40
这是我的代码:
echo "Name,IP" > myFile.csv # let's overwrite, not appending
while IFS= read -r line; do
ipValue= # initialize the value
while IFS= read -r ip; do
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
ipValue+="${ip}-" # append the results with "-"
fi
done < <(dig +short "$line") # assuming the result has multi-line
ipValue=${ipValue%-} # remove trailing "-" if any
if [[ -n $ipValue ]]; then
# if the IP is not empty
echo "$line,$ipValue" >> myFile.csv
fi
done < URLs.txt
我想添加另一列以myFile.csv
保持每个 IP 的开放端口。所以输出会是这样的:
Name, IP, Port
ex1.com, 10.20.30.40, 21/tcp
ex2.com, 20.30.40.30, 20/tcp
ex3.com, 10.45.60.20, 33/tcp
ex4.com, 10.20.30.40, 21/tcp
我想使用 Nmap 来做到这一点。在我从第 2 列中选择一个 IP 地址myFile.csv
并使用 Nmap 找到它的开放端口后,我想将 Nmap 结果写入第 3 列的相应单元格。
此外,如果第二列中还有另一个类似的 IP,我也想为该行编写 Nmap 结果。我的意思是我不想为重复的 IP 再次运行 Nmap。例如,在我的示例中,第 2 列中有两个“10.20.30.40”。我只想对第一个“10.20.30.40”使用 Nmap(并为第二个“10.20.30.40”写入结果,不应为重复的 IP 运行 Nmap)。
为此,我将代码的第一行更改为:
echo "Name,IP,Port" > myFile.csv
还有这里是 Nmap 代码来找到开放的端口:
nmap -v -Pn -p 1-100 $ipValue -oN out.txt
port=$(grep '^[0-9]' out.txt | tr '\n' '*' | sed 's/*$//')
但我不知道下一步该做什么以及如何将这些更改应用到我的代码中。
我将我的代码更新为如下内容:
echo "Name,IP" > myFile.csv # let's overwrite, not appending
while IFS= read -r line; do
ipValue= # initialize the value
while IFS= read -r ip; do
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
ipValue+="${ip}-" # append the results with "-"
fi
done < <(dig +short "$line") # assuming the result has multi-line
ipValue=${ipValue%-} # remove trailing "-" if any
if [[ -n $ipValue ]]; then
# if the IP is not empty
nmap -v -Pn -p 1-100 $ipValue -oN out.txt
port=$(grep '^[0-9]' out.txt | tr '\n' '*' | sed 's/*$//')
echo "$line,$ipValue,$port" >> myFile.csv
fi
done < URLs.txt
但是这样,Nmap 也被用于查找重复 IP 的开放端口,但我不想要这个。我应该怎么办?
解决方案
这是您的脚本的修改版本,大致可以满足您的需求:
#!/usr/bin/env bash
# cache maps from IP addresses to open ports
declare -A cache
getports() {
local ip=$1
nmap -v -Pn -p 1-100 "$ip" -oG - \
| awk -F '\t' '
/Ports:/ {
n = split($2, a, /,? /)
printf "%s", a[2]
for (i = 3; i <= n; ++i)
printf ":%s", a[i]
}
'
}
{
echo 'Name,IP,Port'
while IFS= read -r url; do
# Read filtered dig output into array
readarray -t ips < <(dig +short "$url" | grep -E '^([0-9]+\.){3}[0-9]+$')
# Build array of open ports
unset ports
for ip in "${ips[@]}"; do
ports+=("${cache["$ip"]:=$(getports "$ip")}")
done
# Output
printf '%s,%s,%s\n' \
"$url" \
"$(IFS='-'; echo "${ips[*]}")" \
"$(IFS='-'; echo "${ports[*]}")"
done < URLs.txt
} > myFile.csv
该行将过滤后的输出从IP 地址数组中readarray
读取;dig
如果该数组的长度为零,则跳过循环的其余部分。
然后,对于ips
数组中的每个元素,我们得到端口。为避免nmap
在我们之前看到过 IP 地址时调用,我们使用参数扩展:如果非空,则使用它,否则调用该函数并将输出存储在关联数组中。${parameter:=word}
${cache["$ip"]}
getports
cache
getports
调用我们以前从未见过的 IP 地址;我使用-oG
(" grepable output ") 使解析更容易。awk 命令过滤包含 的行Ports:
,看起来像
Host: 52.94.225.242 () Ports: 80/open/tcp//http/// Ignored State: closed (99)
带有制表符分隔的字段。然后,我们拆分正则表达式的第二个字段/,? /
(可选逗号后跟一个空格)并存储结果数组的第一个字段以外的所有字段,用冒号分隔。
最后,我们打印这行 CSV 数据;如果ips
或ports
包含多个元素,我们想用 连接元素-
,这是通过IFS
在命令替换中设置然后用 打印数组来实现的[*]
。
初始echo
和循环被分组在花括号中,因此输出重定向必须只发生一次。