首页 > 解决方案 > 为列的重复值写入相同的结果

问题描述

我真的是 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 的开放端口,但我不想要这个。我应该怎么办?

标签: bash

解决方案


这是您的脚本的修改版本,大致可以满足您的需求:

#!/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"]}getportscache

getports调用我们以前从未见过的 IP 地址;我使用-oG(" grepable output ") 使解析更容易。awk 命令过滤包含 的行Ports:,看起来像

Host: 52.94.225.242 ()  Ports: 80/open/tcp//http///     Ignored State: closed (99)

带有制表符分隔的字段。然后,我们拆分正则表达式的第二个字段/,? /(可选逗号后跟一个空格)并存储结果数组的第一个字段以外的所有字段,用冒号分隔。

最后,我们打印这行 CSV 数据;如果ipsports包含多个元素,我们想用 连接元素-,这是通过IFS在命令替换中设置然后用 打印数组来实现的[*]

初始echo和循环被分组在花括号中,因此输出重定向必须只发生一次。


推荐阅读