如何使用 AWK 将某些列从一个 CSV 文件提取到另一个文件?

如何使用 AWK 将某些列从一个 CSV 文件提取到另一个文件?

我有一个非常大的 CSV 日志文件,其中包含如下字段:

aaa=somedata1,bbb=somedata2,ccc=somedata3,eee=somedata5,hhh=somedata8
aaa=somedata1,ddd=somedata4,fff=somedata6,hhh=somedata8
aaa=somedata1,bbb=somedata2,hhh=somedata8,ggg=somedata9,jjj=somedata11

该文件的问题在于,当没有值时,生成设备甚至不包含“fieldname=”,因此,由于缺少字段,CSV 看起来是无序的(因此,每次缺少一个字段时,其余的字段都会被删除)。当前字段被拖到 CSV 的左侧)。

我的想法是使用 AWK 仅提取某些相关的列,并且我还需要将其输出到新的 CSV 中。

例如,在上面的示例中,我想提取包含字段“aaa”和“hhh”的所有列,以使新的 CSV 如下所示:

aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

但是,我有两个问题:

  1. 我不知道如何在 AWK 中查找多个条件(我什至尝试将需要的字段/关键字的名称写到 TXT 文件中并在 AWK 中读取它,但我做不到)。
  2. 每次我尝试打印结果列时,新的 CSV 只打印一个巨大的列,而且我似乎找不到打印列分隔的方法。

感谢任何帮助!

---编辑1---

是的,我尝试使用一些单独的 AWK 命令,如下所示:

awk '{for (i=1;i<=NF;i++) if ($i ~ /aaa/) { print $i}}' > aaa.csv
awk '{for (i=1;i<=NF;i++) if ($i ~ /hhh/) { print $i}}' > hhh.csv

然后尝试使用(当然,我总共有 10 个不同的列,我有兴趣提取,但为了简洁起见,我在示例中只放了 2 个):

paste -d "," aaa.csv hhh.csv > Allcolumns.csv

---编辑2---

我总共有大约 10 个相关列,我想将它们提取到一个新文件中,因为原始文件是一个日志,我确保哪些列出现在所有行上,而这些列是我实际需要的。如果万一它们没有出现在原始文件中,我想最好的做法是让最终文件反映类似“aaa,hhh,,iii”的内容。

答案1

每当您的数据中有 tag=value 对时,我发现最好首先创建一个数组来保存该映射(tag2val[]如下),然后您可以通过标签(也称为名称或键)引用所有值。

在所有 Unix 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
BEGIN {
    FS = OFS = ","
    numTags = split("aaa,hhh",tags)
}
{
    delete tag2val
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        tag2val[tag] = $i
    }

    for (i=1; i<=numTags; i++) {
        tag = tags[i]
        printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
    }
}

$ awk -f tst.awk file
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

如果您想在每一行上打印所有可能的字段,那么这是一种 2 遍方法,其中第一遍只是识别每行中的所有可能字段:

$ cat tst.awk
BEGIN {
    FS = OFS = ","
}
NR==FNR {
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        if ( !seen[tag]++ ) {
            tags[++numTags] = tag
        }
    }
    next
}
{
    delete tag2val
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        tag2val[tag] = $i
    }

    for (i=1; i<=numTags; i++) {
        tag = tags[i]
        printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
aaa=somedata1,bbb=somedata2,ccc=somedata3,eee=somedata5,hhh=somedata8,,,,
aaa=somedata1,,,,hhh=somedata8,ddd=somedata4,fff=somedata6,,
aaa=somedata1,bbb=somedata2,,,hhh=somedata8,,,ggg=somedata9,jjj=somedata11

如果您只想打印所有行中出现的字段:

$ cat tst.awk
BEGIN {
    FS = OFS = ","
}
NR==FNR {
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        cnt[tag]++
    }
    next
}
FNR==1 {
    for (tag in cnt) {
        if ( cnt[tag] == (NR-1) ) {
            tags[++numTags] = tag
        }
    }
}
{
    delete tag2val
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        tag2val[tag] = $i
    }

    for (i=1; i<=numTags; i++) {
        tag = tags[i]
        printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
hhh=somedata8,aaa=somedata1
hhh=somedata8,aaa=somedata1
hhh=somedata8,aaa=somedata1

如果字段输出的顺序很重要,这也是一个简单的调整,例如,要保留输入顺序,您只需在第一个块中创建一个数组,以将递增计数映射到每个新标签,如下所示:

$ cat tst.awk
BEGIN {
    FS = OFS = ","
}
NR==FNR {
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        if ( !cnt[tag]++ ) {
            order[++totTags] = tag
        }
    }
    next
}
FNR==1 {
    for (i=1; i<=totTags; i++) {
        tag = order[i]
        if ( cnt[tag] == (NR-1) ) {
            tags[++numTags] = tag
        }
    }
}
{
    delete tag2val
    for (i=1; i<=NF; i++) {
        tag = $i
        sub(/=.*/,"",tag)
        tag2val[tag] = $i
    }

    for (i=1; i<=numTags; i++) {
        tag = tags[i]
        printf "%s%s", tag2val[tag], (i<numTags ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

答案2

如果您可以选择使用磨坊主,那么您的数据完全采用 Miller 的dkvp(键值对)格式,您可以cut直接按字段名称:

$ mlr --dkvp cut -f aaa,hhh file.csv
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

或者,您可以使用以下命令恢复丢失的字段unsparsify

$ mlr --dkvp unsparsify file.csv
aaa=somedata1,bbb=somedata2,ccc=somedata3,eee=somedata5,hhh=somedata8,ddd=,fff=,ggg=,jjj=
aaa=somedata1,bbb=,ccc=,eee=,hhh=somedata8,ddd=somedata4,fff=somedata6,ggg=,jjj=
aaa=somedata1,bbb=somedata2,ccc=,eee=,hhh=somedata8,ddd=,fff=,ggg=somedata9,jjj=somedata11

答案3

awk -F "," '{for(i=1;i<=NF;i++){if($i ~ /aaa|hhh/){print $i}}}' filename|sed "N;s/\n/,/g"

输出

aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8
aaa=somedata1,hhh=somedata8

相关内容