【问题标题】:using awk, how to print multiple input lines to a single csv row?使用 awk,如何将多个输入行打印到单个 csv 行?
【发布时间】:2017-10-14 19:18:26
【问题描述】:

在工作中,我的角色正在从桌面支持转变为应用程序支持,所以这对我来说很新鲜。作为该过程的一部分,我的任务是学习 Linux 的基础知识,很快就会学习 SQL。我最好用真实的例子来学习,但我学得还不够好!

我们有一个我想为用户自动化的流程。我可以随时联系供应商,要求他们更改流程,但我正在努力充分利用这段学习时间。

我们收到一封 HTML 电子邮件,其中显示了导入过程的例外情况,用户检查表以找出组织的 ID,然后通过组织的 ID 在另一个表中查找异常。我想做的是将匹配所需的信息拆分为 CSV。我的最终目标是进行 SQL 查询以获取 .CSV 并从一个表中查找,然后从另一个表中查找,然后为他输出异常的原因。

我想坚持使用 awk,因为我已经花了几天时间试图真正把它搞定,但我知道什么?我发现其他看起来像在 VB 中执行此操作的帖子,以使该过程与 Outlook 和 Excel 集成会更好,但是当您拥有的唯一工具是锤子时...

电子邮件输入文件在 awk 中如下所示:

<strong>Request Made By: </strong>Joe Schmoe(<a href="mailto:joeschmoe@joemail.com">joeschmoe@joemail.com</a>)<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Request from: </strong>Kentucky - Highway<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Receiving Ministry:</strong> District Project<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Receiving Ministry Name:</strong> Papua New Guinea Hospital<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Intent of Gift</strong> Use funds toward building and renovating hospitals.<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Gift Type:</strong> Cash<o:p></o:p></li><li class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;mso-list:l0 level1 lfo1">
<strong>Amount:</strong> 500.00<o:p></o:p></li></ul>

相关信息为邮箱地址、接收部门名称和金额;那些应该显示一个独特的条目。我显然不明白分组命令,所以我吹了很多。我的问题是我无法从第二次搜索(接收部门名称)中获得与第一次搜索(电子邮件地址)相同的结果。我的目标是:

joeschmoe@joemail.com,Papua New Guinea Hospital,500.00

然后为同一电子邮件中的下一个错误换行。美元输入可能有也可能没有小数。

到目前为止,这是我拼凑起来的:

#!/bin/bash

awk '/Request Made By:/' ~/emailSort/FW* | awk -F'(' '{print $2}' | awk -F'<' '{print $1}' | awk -F'\n' 'BEGIN{OFS=",";} {print $1}' > output.csv
awk '/Receiving Ministry Name:/' ~/emailSort/FW* | awk -F': ' '{print $2}' >> output.csv
awk '/Amount:/' ~/emailSort/FW* | awk -F'(' '{print $2}' >> output.csv

这是我的结果:

joeschmoe@joemail.com

Papua New Guinea Hospital

在那之后还有一些空白。 自从我被这个基本问题挂断以来,我还没有真正触及第三行。这次的文件名为FW_FTM Point To Point。

如果您不介意解释“为什么”?在任何代码或语法背后,我将不胜感激。我正在虚拟机上的 Ubuntu 17.04 的 shell 脚本中尝试此操作。

【问题讨论】:

    标签: sql bash csv awk


    【解决方案1】:

    awk 单行方法:

    awk -F'<[^><]+>' 'BEGIN{s=""; OFS=","}{if($2~/Request Made By:/) s=s$4; 
        else if($2~/Receiving Ministry Name:|Amount:/) s=s OFS $3} END {print s}' file
    

    输出:

    joeschmoe@joemail.com, Papua New Guinea Hospital, 500.00
    

    -F'&lt;[^&gt;&lt;]+&gt;' - 按标签分隔字段 (&lt;tagname&gt;)

    【讨论】:

    • @dawg 该脚本没有任何特定于 gawk 的内容,它适用于任何 awk。你认为哪一部分是 gawk 特有的?
    【解决方案2】:

    每当您的数据包含名称->值映射时,我发现最容易使用它们,首先创建一个数组来保存这些映射(下面的n2v[]),然后您可以通过它们的名称访问值来做任何你想做的事情和他们一样:

    $ cat tst.awk
    BEGIN { FS=":? *<[^<>]+> *"; OFS="," }
    {
        gsub(OFS," ")
        name = $2
        value = (name == "Request Made By" ? $4 : $3)
        n2v[name] = value
    }
    name == "Amount" {
        print n2v["Request Made By"], n2v["Receiving Ministry Name"], n2v["Amount"]
        delete n2v
    }
    
    $ awk -f tst.awk file
    joeschmoe@joemail.com,Papua New Guinea Hospital,500.00
    

    以下显示了这种方法如何使您可以使用所有名称->值映射:

    $ cat tst.awk
    BEGIN { FS=":? *<[^<>]+> *"; OFS="," }
    {
        gsub(OFS," ")
        name = $2
        value = (name == "Request Made By" ? $4 : $3)
        n2v[name] = value
    }
    name == "Amount" {
        # print n2v["Request Made By"], n2v["Receiving Ministry Name"], n2v["Amount"]
        for (name in n2v) {
            print name, n2v[name]
        }
        delete n2v
    }
    
    $ awk -f tst.awk file
    Request from,Kentucky - Highway
    Request Made By,joeschmoe@joemail.com
    Amount,500.00
    Gift Type,Cash
    Intent of Gift,Use funds toward building and renovating hospitals.
    Receiving Ministry Name,Papua New Guinea Hospital
    Receiving Ministry,District Project
    

    由于您的输出是 CSV,如果您可以在要输出的任何输入字段中使用逗号,那么您必须使用 gsub(OFS," ") 来处理它,就像我用其他字符替换它们一样,或者添加输出时在每个字段周围加上双引号(如果您接下来要执行的操作支持在带引号的字段中使用分隔符)或其他您自行决定的内容...

    【讨论】:

    • 哦,当然是变量!我太沉迷于“先处理这个,然后处理那个”,以至于我忘记了这一点!谢谢,我在真实交易上尝试了代码,这是一个前锋的前锋,它工作得很好,没有被任何误报挂断。现在我将尝试确保我理解它为什么有效。谢谢,埃德。
    • 不客气,一旦您对答案感到满意,请参阅stackoverflow.com/help/someone-answers 了解下一步该做什么。
    【解决方案3】:

    我不是 awk 专家,但是当我想在同一行打印多个搜索时,我使用单个 awk 语句并将每个收集的值分配给一个变量:

    cat file.txt |awk '
    /line 1 search/ {var1=$1} 
    /line 2 search/ {var2=$1} 
    /line 3 search/ {var3=$1} 
    {if (var1 && var2 && var3) 
        {print var1","var2","var3
        var1=""
        var2=""
        var3=""}
     }'
    

    这样。请注意,我将变量重新设置为空,以确保我不会一直得到打印输出或部分输出。这是一个工作示例:

    # cat afile
    In this file the name is Joe Schmoe
    On this separate line the place is Papua New Guinea Hospital
    The amount of thing is 500.00
    In this file the name is Jane Lane
    On this separate line the place is Virginia Mason
    The amount of thing is 100.00
    
    #cat afile |awk -F" is " '
    /name/ {name=$2} 
    /place/ {place=$2} 
    /amount/ {amount=$2} 
    {if (name && place && amount) 
        {print name","place","amount
        name=""
        place=""
        amount=""}
    }'
    
    Joe Schmoe,Papua New Guinea Hospital,500.00
    Jane Lane,Virginia Mason,100.00
    

    但是,通过这种方式,您的文件的解决方案会复杂得多,因为它需要您使用像 match()substr() 这样的字符串操作函数来打印出您正在寻找的内容,而不是别的:

    https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html

    我想如果我被要求这样做,我可能会awk -F"&lt;\/strong&gt;" 然后使用split() 使用“(”作为名称,使用“split() 制作的数组。

    【讨论】:

      猜你喜欢
      • 2014-05-14
      • 2016-06-30
      • 1970-01-01
      • 1970-01-01
      • 2020-11-18
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      相关资源
      最近更新 更多