【问题标题】:Print columns with a certain header of a CSV file using AWK使用 AWK 打印具有 CSV 文件特定标题的列
【发布时间】:2021-08-20 04:43:35
【问题描述】:

Current CSV file

Preferred output

我试图从我的 CSV 文件中选择特定的列并打印出来。我想打印标题中包含“未发布的最终分数”字符串的所有列。

我可以定制两段代码。一件给我所有正确的标题,但缺少列的正文。 另一部分只给了我第一个带有正确标题的完整列。

我是 AWK 的新手,并尽我所能为我量身定制一些东西,但我没有成功。

有人能指导我吗?

干杯 乙 :)

请在此处查看我的代码:

这是只给我正确的标题而不是列的正文的代码:

{for(i=1;i<=NF;i++)
    {if ($i ~ /Unposted Final Score/)
    {print $i}
    }
}

输出:

A1. Unposted Final Score
A2. Proposal and Storyboard Unposted Final Score

==================

这是给我第一列而不是其余列的代码,其中包含“未发布的最终分数”:

{ for (i=1;i<=NF;++i) if ($i ~ /Unposted Final Score/) { n=i; break }} { print $n }

输出:

A1. Unposted Final Score
56
95
90
93
0
80
61

======================

我当前的 CSV 文件:

ID,Section,A1. Final Score,A1. Unposted Final Score,A2. Current Score,A2. Proposal and Storyboard Unposted Final Score
4836,Sydney A,1,56,,34
5376,Sydney A,2,95,0,1
4760,Sydney A,3,90,,30
4675,Sydney A,4,93,3,0
4873,Sydney B,0,0,33,50
4848,Sydney A,80,80,0,0
4755,Sydney A,61,61,,0

首选 CSV 文件:

A1. Unposted Final Score,A2. Proposal and Storyboard Unposted Final Score
56,34
95,1
90,30
93,0
0,50
80,0
61,0

【问题讨论】:

  • 您好,欢迎来到 SO。请了解如何格式化您的问题here。事实上,你无法理解你想要达到的目标。

标签: awk


【解决方案1】:

在每个 Unix 机器上的任何 shell 中使用任何 awk,以下内容将按照输入中出现的相同顺序稳健、高效且可移植地打印您想要的字段:

$ cat tst.awk
BEGIN { FS=OFS="," }
NR==1 {
    for (i=1; i<=NF; i++) {
        if ( $i ~ /Unposted Final Score/ ) {
            out2in[++numOutFlds] = i
        }
    }
}
{
    for (o=1; o<=numOutFlds; o++) {
        i = out2in[o]
        printf "%s%s", $i, (o<numOutFlds ? OFS : ORS)
    }
}

$ awk -f tst.awk file
A1. Unposted Final Score,A2. Proposal and Storyboard Unposted Final Score
56,34
95,1
90,30
93,0
0,50
80,0
61,0

另见sh Break CSV files by value of column name matched while retaining header

【讨论】:

    【解决方案2】:

    假设您的数据在一个名为 u.csv 的文件中,您可以简单地试试这个:

    cat u.csv | awk -F',' '{printf $4 "," $6 "\n" }'
    

    它会给你这个输出:

    A1. Unposted Final Score,A2. Proposal and Storyboard Unposted Final Score 
    56,34
    95,1
    90,30
    93,0
    0,50
    80,0
    61,0
    

    解释:

    awk -F','
    

    将使用, 作为分隔符分割你的行。

    IE,第一行:

    4836,Sydney A,1,56,,34
    

    因此,对于awk,值将存储在:

    $1 -> 4836
    $2 -> Sydney A
    $3 -> 1
    $4 -> 54
    $5 -> 
    $6 -> 34
    

    之后,您只需使用awk 内置的printf 函数即可输出您想要的输出。

    【讨论】:

    • 请参阅porkmail.org/era/unix/award.html 了解为什么不使用cat file | awk 并始终对任何输入数据使用printf "%s", $i 而不是print $i,因为当输入包含诸如@987654335 之类的printf 格式字符时,后者将失败@。不要硬编码像"," 这样的分隔符,只需设置和使用OFS,也不要硬编码您希望ORS 设置的字符"\n",只需像awk 设计的那样使用ORS。所以cat u.csv | awk -F',' '{printf $4 "," $6 "\n" }' 应该是awk 'BEGIN{FS=OFS=","} {print $4, $6}' u.csv
    【解决方案3】:

    以下内容在 POSIX 模式下使用 GNU awk 5.1.0 和 macOS 附带的 awk 20070501 进行了测试。可能是它与其他版本的awk 完全不兼容。

    您的第一次尝试是一个很好的起点。只需在第一行做类似的事情来记录要打印的列的索引,然后在包括第一行在内的所有记录上,只打印相应的字段:

    $ cat foo.awk
    NR==1 {for(i=1;i<=NF;i++) if($i~/Unposted Final Score/) idx[++n]=i}
    {for(i=1;i<=n;i++) printf("%s%s", $(idx[i]), (i==n)?ORS:",")}
    
    $ awk -f foo.awk -F, foo.csv
    A1. Unposted Final Score,A2. Proposal and Storyboard Unposted Final Score
    56,34
    95,1
    90,30
    93,0
    0,50
    80,0
    61,0
    

    解释:

    • 我们使用-F, 选项将输入字段分隔符设置为逗号,而不是默认值(空格)。

    • NR==1 条件仅对第一条记录(标题)为真。在这个块中,我们将所有匹配Unposted Final Score 的字段索引存储在idx 数组中。变量n 用于存储匹配字段的数量,我们将使用它来打印输出。我们可以使用{n=n+1; idx[n]=i} 代替idx[++n]=i,这可能更容易理解(变量的数值自动初始化为0)。

    • 第三个块在所有行上执行(无条件)。它打印索引在 idx 数组中的所有字段,后跟一个逗号,除了最后一个打印的字段后跟输出记录分隔符(ORS,默认为换行符)。

    【讨论】:

    • 这将打乱输出列的顺序,并可以在应该全部在一行的字段中打印一个换行符,由for (i in idx)提供,请参阅gnu.org/software/gawk/manual/gawk.html#Scanning-an-Array
    • @EdMorton 对。它有效,但不能保证,它也可能是偶然的。使用 GNU awk,这可以通过 BEGIN{PROCINFO["sorted_in"] = "@ind_num_asc"} 进行控制。但我会改为修改我的答案以获得更好的可移植性。
    • 关于“它有效” - 需要明确的是,它并不是真的有效,它只是碰巧在特定机器上使用特定 awk 版本为特定输入集产生预期输出。对我来说,“它有效”意味着它总是会使用任何 awk 从任何输入产生预期的输出,但事实并非如此。
    • 这就是我所说的偶然
    • 现在和我的答案一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 2013-10-26
    相关资源
    最近更新 更多