我有一个非常大的 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
但是,我有两个问题:
- 我不知道如何在 AWK 中查找多个条件(我什至尝试将需要的字段/关键字的名称写到 TXT 文件中并在 AWK 中读取它,但我做不到)。
- 每次我尝试打印结果列时,新的 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