首页 > 解决方案 > 当值是多行时解析 CSV 记录

问题描述

源文件如下所示:

"google.com", "vuln_example1
vuln_example2
vuln_example3"
"facebook.com", "vuln_example2"
"reddit.com", "stupidly_long_vuln_name1"
"stackoverflow.com", ""

我一直试图让输出变成这样,但换行符似乎给我带来了无穷无尽的问题。我正在使用“读取时行”作业来执行此操作,因为我对列进行了一些处理(例如,本示例中的漏洞计数和 url)。这是输出到詹金斯作业(yuk)中。

问题的基本总结是让 csv 中的换行符输出到第三列,同时保留表结构。我在下面有一个所需输出的奇怪示例。

||hostname         ||Vulnerability count|| Vulnerability list    || URL                       ||
|google.com        |3                   |vuln_example1            |http://cve.com/vuln_example1|
|                  |                    |vuln_example2            |http://cve.com/vuln_example2|
|                  |                    |vuln_example3            |http://cve.com/vuln_example3|
|facebook.com      |1                   |vuln_example2            |http://cve.com/vuln_example2|
|reddit.com        |1                   |stupidly_long_vuln_name1 |http://cve.com/stupidly_long_vuln_name1|
|stackoverflow.com |0                   |                         ||

看着这个......我有一种感觉,通过显示一些代码和示例输出可能会更容易。

标签: bash

解决方案


使用下面的命令行解析您的输入会使问题变得更容易(我假设输入是正确的):

perl -0777 -pe 's/([^"])\s*\n/\1 /g ; s/[",]//g'  < sample.txt

此行调用 Perl 来执行两个正则表达式替换:

  • s/([^"])\s*\n/\1 /g: 如果它没有以引号终止"(即,如果主机条目,所有漏洞尚未完成),则此替换将删除行尾。
  • s/[",]//g删除所有剩余的引号和逗号。

对于像这样的每个主机条目:

"google.com", "vuln_example1
vuln_example2
vuln_example3"

你会得到:

google.com vuln_example1 vuln_example2 vuln_example3

然后你可以假设每一行都有一个主机和一组漏洞。

下面给出的示例将漏洞存储在一个数组中并循环遍历它,格式化和打印每一行:

# Replace this by your custom function
# to get an URL for a given vulnerability
function get_vuln_url () {
    # This just displays a random url for an non-empty arg 
    [[ -z "$1" ]] || echo "http://host/$1.htm"
}

# Format your line (see printf help)
function print_row () {
    printf "%-20s|%5s|%-30s|%s\n" "$@"
}

# The perl line reformat 
perl -0777 -pe 's/([^"])\s*\n/\1 /g ; s/[",]//g'  < sample.txt |
    while read -r line ; do
        arr=(${line})
        print_row "${arr[0]}" "$((${#arr[@]} - 1))" "${arr[1]}" "$(get_vuln_url  ${arr[1]})"
        #echo -e "${arr[0]}\t|$vul_count\t|${arr[1]}\t|$(get_vuln_url  ${arr[1]})"
        for v in "${arr[@]:2}" ; do
            print_row " " " " "$v" "$(get_vuln_url  ${arr[1]})"
        done
    done

输出:

google.com          |    3|vuln_example1                 |http://host/vuln_example1.htm
                    |     |vuln_example2                 |http://host/vuln_example1.htm
                    |     |vuln_example3                 |http://host/vuln_example1.htm
facebook.com        |    1|vuln_example2                 |http://host/vuln_example2.htm
reddit.com          |    1|stupidly_long_vuln_name1      |http://host/stupidly_long_vuln_name1.htm
stackoverflow.com   |    0|                              |

更新。 如果您没有 Perl,并且您的文件没有表格,则可以使用此命令作为解决方法:

tr '\n' '\t' < sample.txt | sed -r -e 's/([^"])\s*\t/\1 /g' -e 's/[",]//g'  -e 's/\t/\n/g'
  • tr '\n' '\t'用表格替换所有行尾
  • sedpart 的作用类似于 Perl 行,除了它处理制表符而不是行尾并将制表符恢复到行尾。

推荐阅读