【问题标题】:Convert list of FQDN and IPs to two column CSV将 FQDN 和 IP 列表转换为两列 CSV
【发布时间】:2015-12-31 05:36:40
【问题描述】:

我想要一个这样的列表:

example.com
1.2.3.4
ftp.example.com
2.3.4.5
3.4.5.6
www.example.com
4.5.6.7
5.6.7.8
6.7.8.9

并解析为逗号分隔的 CSV 格式,以便在流行的电子表格程序中打开时,父 FQDN 位于 A 列,子 IP 位于 B 列。

我想使用本机 Linux 二进制文件来执行此操作,这样我就可以烘焙到现有的 BASH 脚本中。

欢迎任何帮助,并提前致谢。

【问题讨论】:

  • 不知道为什么我的问题被否决了。至少发表评论说明原因。
  • 感谢 potongskmrx。两个很好的答案。我最后选择了 sed,因为它更简洁地附加到现有的 sed 参数中。
  • sed 和 shell 循环都不是正确的方法,awk 是。将 sed 用于除单个行上的简单替换之外的任何内容都可以作为一种心理锻炼,但不要真正在代码中使用它,您可能有一天必须再次查看并尝试增强。文本处理应避免外壳循环,例如见unix.stackexchange.com/q/169716/133219

标签: linux bash csv awk sed


【解决方案1】:

编辑:我读错了问题。我下面的解决方案先打印主机名,然后打印 IP 地址列表,而不是主机名 + ip 地址对列表。

我会使用以下逻辑:对于每一行输入,

  1. 如果文本中包含 IP 地址以外的内容,则打印一个换行符,然后打印文本。第一行文本不打印换行符。
  2. 否则,打印一个逗号,然后是文本

例子:

Perl:

perl -npe 'chomp;  $_ = /[^\d.]/ ? "$p$_" : ",$_"; $p="\n"'

重击:

#!/bin/bash

while read line; do
    if [[ $line =~ [^0-9.] ]]; then
        echo -en "$pre$line"
    else
        echo -n ",$line"
    fi
    pre="\n"
done

【讨论】:

  • 我无法评论 perl 脚本,因为据我所知,它可能是青霉素的化学式,但从不使用 shell 循环来操作文本。发明 shell 来操作文件和进程以及对工具的调用排序的人也发明了 awk 来调用 shell 来操作文本。与等效的 awk 脚本相比,shell 循环的健壮性、可移植性和效率都低得多。有关该主题的讨论,请参阅unix.stackexchange.com/questions/169716/…
【解决方案2】:

这可能对你有用(GNU sed):

sed -r '/[[:alpha:]]/h;//d;G;s/(.*)\n(.*)/\2,\1/' file

如果该行包含字母字符,即是地址,则将其存储在保留空间中,然后将其删除。否则,将地址附加到当前行,然后将两个字段交换为 , 替换换行符并打印。

【讨论】:

    【解决方案3】:

    sed 用于在单个行上进行简单替换,仅此而已。如果您使用的是 s、g 和 p(带 -n)以外的 sed 结构,那么您使用的结构在 1970 年代中期 awk 被发明时就已经过时了。

    $ awk '/^[[:alpha:]]/{f=$0;next} {print f","$0}' file
    example.com,1.2.3.4
    ftp.example.com,2.3.4.5
    ftp.example.com,3.4.5.6
    www.example.com,4.5.6.7
    www.example.com,5.6.7.8
    www.example.com,6.7.8.9
    

    请注意这是多么清晰和简单,因为 awk 有变量,而 sed 没有。巧合的是,如果您关心它,它也比 sed 方法稍微简单一些,而且我敢打赌,如果您的文件很大,它的执行速度会更快。它还可以在所有操作系统上的所有 POSIX(由于 POSIX 字符类)awks 上可移植地工作,它不是特定于 GNU 的。

    为了解决下面的评论,如果您希望每个 FQDN 的所有 IP 地址都在一行上,那么这是一种方法:

    $ cat tst.awk
    /^[[:alpha:]]/ { recs[++numFqdns] = $0; next }
    { recs[numFqdns] = recs[numFqdns] "," $0 }
    END {
        for (fqdnNr=1; fqdnNr<=numFqdns; fqdnNr++) {
            print recs[fqdnNr]
        }
    }
    
    $ awk -f tst.awk file
    example.com,1.2.3.4
    ftp.example.com,2.3.4.5,3.4.5.6
    www.example.com,4.5.6.7,5.6.7.8,6.7.8.9
    

    另外,这个 shell 脚本的直接 awk 翻译来自 skmrx 的回答:

    while read line; do
        if [[ $line =~ [^0-9.] ]]; then
            echo -en "$pre$line"
        else
            echo -n ",$line"
        fi
        pre="\n"
    done
    

    应该是:

    awk '{
        if (/[^0-9.]/) {
            printf "%s%s", pre, $0
        }
        else {
            printf ",%s", $0
        }
        pre="\n"
    }'
    

    但是您永远不会真正在 awk 中编写,而是在 awk 中编写这种类型的逻辑的惯用方式是:

    awk '{ printf "%s%s", (/[^0-9.]/ ? pre : ""), $0; pre=RS }'
    

    您可以添加 END{print ""} 以打印 shell 脚本中缺少的最后一个换行符。

    【讨论】:

    • 问题略有不同。请求者希望主机名下的所有 IP 出现在同一行,用逗号分隔。你可以更新你的答案吗?很高兴看到它在 awk 中是如何完成的!
    • 我不同意,这正是所要求的。 OP 说他想在 Excel 或类似文件中打开结果,并在一列中查看 FQDN,在另一列中查看 IP 地址。如果我们将与 FQDN 关联的所有 IP 地址以逗号分隔的形式列在一行上,那么 Excel 将在 1 列中包含 FQDN,但随后会出现各种数量的其他列 - 这实际上是无用的。我在上面输出的也是 OP 从他接受为正确答案的 sed 脚本中得到的输出。话虽如此,我肯定会发布一个 awk 脚本,以另一种方式对其进行格式化。给我一点时间...
    • @skmrx 好的,我发布了一个脚本来做到这一点。
    • 你是对的——你的解决方案就是所要求的;对于那个很抱歉。看起来我读错了问题。但是感谢您花时间编写附加脚本!
    • @skmrx 我添加了另一种 awk 方法,以便您可以更清楚地看到 shell 脚本和 awk 脚本之间的关系。
    猜你喜欢
    • 2018-06-23
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 2016-01-15
    • 2020-01-21
    • 1970-01-01
    相关资源
    最近更新 更多