【问题标题】:Compare the values of 2 files and merge the similar entries比较2个文件的值并合并相似的条目
【发布时间】:2020-05-18 08:41:31
【问题描述】:

我是 bash 脚本的新手。我有 2 个文件:一个包含 IP 地址列表的文本文件,一个包含 2 列的 .csv 文件,第二列包含 IP 地址。我想将文本文件的每一行(每个 IP)与 .csv 文件第二列的所有元素进行比较。如果 .csv 文件中有多个具有相同 IP 的条目,我想将它们的第一个字段合并到一行中。例如:

      column1       column2
row1: example.com   1.1.1.1
row2: example2.com  1.1.1.1

我想把它转换成这个:

      column1       column2
row1: example.com   1.1.1.1
      example2.com  

我已将值写入 .csv 和 .txt 文件,但我不知道如何比较和合并相似的值。我找到了这个命令,但不明白如何应用它:

comm -- 选择或拒绝两个文件共有的行

【问题讨论】:

  • .csv?你的字段分隔符是什么?
  • @cyrus 字段用“,”分隔
  • 附带说明:在我看来,dig 输出的处理过于复杂,我很好奇 while read line; do echo $line,$(dig +short $line); done < URLs 哪里让你失望了?
  • 请显示原始输入文件内容和实际所需的输出(即,删除columnNrowN 标签,因为这只是混淆);至于示例输出......你真的想要在不同的行上的 2x 地址吗?如果没有,则更新问题以显示您正在寻找的实际输出
  • @markp 是的,我想要一行有 2 列。在第一列中,我想要“example.com\n example2.com”,在第二列中,我想要 IP 地址。

标签: bash csv ipv4


【解决方案1】:

假设:

  • csv 文件(有 2 列:域名 + ip 地址)使用逗号 (,) 作为分隔符(示例数据中未显示,但 OP 在评论中提到了这一点)
  • 没有提到按任何特定顺序对最终输出进行排序的任何要求,因此我将以与以下相同的顺序打印输出:
    • ips 出现在第一个文件中
    • 域地址出现在 csv 文件中
  • 没有为第一个文件提供示例,所以我假设每行有一个 ip 地址
  • 我不会担心 IP 地址在第一个文件中出现多次的可能性(即,每次 IP 地址出现在第一个文件中时,我们将重复打印相同的匹配域名文件)
  • 任何一个文件中没有另一个文件“匹配”的条目都不会显示在最终输出中

样本数据:

$ cat domain.dat
example.com,1.1.1.1
example3.com,3.4.5.6
example5.com,11.12.13.14
exampleX.com,99.99.99.99    # no matches in ip.dat
example2.com,1.1.1.1
example4.com,11.12.13.14

$ cat ip.dat
1.1.1.1
2.2.2.2                     # no matches in domain.dat
3.4.5.6
7.8.9.10                    # no matches in domain.dat
11.12.13.14
1.1.1.1                     # repeat of an ip address

awk 解决方案首先处理domain.dat 以填充数组(domains[<ipaddress>]=<domainaddress>[,<domainaddress]*),然后处理ip.dat 以确定要打印到标准输出的域地址:

awk -F "," '

# first file: keep track of the longest domain address; to be used by printf

NR==FNR                      { if (length($1) > maxlen) { maxlen=length($1) } }

# first file: if the ip address is already an index in our array then append the current domain address to the array element; skip to next of input

(NR==FNR) && ($2 in domains) { domains[$2]=domains[$2]","$1 ; next }

# first file: first time we have seen this ip address so create a new array element, using the ip address as the array index; skip to next line of input

NR==FNR                      { domains[$2]=$1             ; next}

# second file: if the ip address is an index in our array ...
# split the domain address(es), delimited by comma, into a new array named "arr" ...

( $1 in domains )            { split(domains[$1],arr,",")

                               # set the output line suffix to the ip address

                               sfx=$1

                               # loop through our domain addresses, appending the ip address to the end of the first line; after we print the first domain
                               # address + ip address, reset suffix to the empty string so successive printfs only display the domain address;
                               # the "*" in the format string says to read the numeric format from the input parameters - "maxlen" in this case

                               for (i in arr) { printf "%-*s   %s\n",maxlen,arr[i],sfx ; sfx="" }
                             }
' domain.dat ip.dat

注意:嵌入的 cmets 可以移除以减少混乱。

以上运行结果:

example.com    1.1.1.1
example2.com
example3.com   3.4.5.6
example5.com   11.12.13.14   # example5.com comes before example4.com in domain.dat
example4.com
example.com    1.1.1.1       # repeated because 1.1.1.1 was repeated in ip.dat
example2.com

【讨论】:

  • 优秀的答案,详细的代码和全面的解释。
【解决方案2】:

您可以列出所有 IP,然后遍历这些 IP 以获取与该 IP 对应的域。

#! /bin/bash
set -euo pipefail

FILENAME="$1"

readarray -t ip_addresses<<<"$(cut -d ',' -f 2 "$FILENAME" | sort -u)"

for ip in "${ip_addresses[@]}" ; do
    readarray -t domains_for_ip<<<"$(grep "$ip" "$FILENAME" | cut -d ',' -f 1)"
    echo "${domains_for_ip[*]},$ip"
done

输入文件为

example.com,1.1.1.1
example2.com,1.1.1.1
example3.com,1.1.1.2

你会得到

example.com example2.com,1.1.1.1
example3.com,1.1.1.2

此脚本目前不检查第一个参数 ($1) 是否存在,也无法检查 IP 是否真正唯一(它将 10.0.0.1010.000.000.001 视为两个唯一地址)。它还假设文件中没有奇怪的空格。

【讨论】:

  • 这里的 $FILENAME 是什么?我应该在这里写哪个文件名?
  • $FILENAME 包含文件的名称以及主机名和 IP 地址(在您的原始脚本中,它是 subdomainIP.csv)。我的错,我应该改变它。
【解决方案3】:

类似:

    while read IP; do 
       grep $IP subdomainIP.csv | \
           cut -f1 -d',' | \
           tr "\n" " "| \
           sed 's/ $//'; 
       echo ,$IP; 
    done < ipfile.txt

【讨论】:

    【解决方案4】:

    开始使用 Miller (https://github.com/johnkerl/miller)
    example.com,1.1.1.1
    example2.com,1.1.1.1
    example3.com,1.1.1.2
    

    正在运行

    mlr --csv -N nest --implode --values --across-records -f 1 ipfile.txt >output.txt
    

    你将拥有

    example.com;example2.com,1.1.1.1
    example3.com,1.1.1.2
    

    如果你想要\n分隔的URL,命令是

    mlr --csv -N nest --implode --values --across-records --nested-fs "\n" -f 1 ipfile.txt >output.txt
    

    【讨论】:

    • 我应该在哪里导入“ipfile.txt”?
    • 为什么不加评论就投反对票?不行吗?
    • @helen 你试过用米勒吗?不适合你吗?
    猜你喜欢
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多