从算法的角度来看,我将采取以下步骤:
-
处理标题:
- 读取所有输入文件的所有标题并提取所有列名
- 按照您想要的顺序对列名称进行排序
- 创建一个查找表,当给定字段编号时返回列名(
h[n] -> "name"
)
-
处理文件:在标头之后,您可以重新处理文件
- 读取文件头
- 创建一个查找表,在给定列名时返回字段编号。关联数组在这里很有用:(
a["name"] -> field_number
)
-
处理文件的其余部分
- 循环合并文件的所有字段
- 获取列名
h
- 检查列名是否在
a
,如果不打印-
,如果是,则打印对应的字段号a
.
这可以通过使用扩展的 GNU awk 轻松完成nextfile
and asorti
. The nextfile
函数允许我们只读取文件头并移动到下一个文件,而不处理整个文件。由于我们需要处理该文件两次(步骤 1 读取文件头,步骤 2 读取文件),因此我们将要求 awk 动态操作其参数列表。每次处理文件头时,我们都会将其添加到参数列表的末尾ARGV
所以它可以用于step 2
.
BEGIN { s="-" } # define symbol
BEGIN { f=ARGC-1 } # get total number of files
f { for (i=1;i<=NF;++i) h[$i] # read headers in associative array h[key]
ARGV[ARGC++] = FILENAME # add file at end of argument list
if (--f == 0) { # did we process all headers?
n=asorti(h) # sort header into h[idx] = key
for (i=1;i<=n;++i) # print header
printf "%s%s", h[i], (i==n?ORS:OFS)
}
nextfile # end of processing headers
}
# Start of processing the files
(FNR==1) { delete a; for(i=1;i<=NF;++i) a[$i]=i; next } # read header
{ for(i=1;i<=n;++i) printf "%s%s", (h[i] in a ? $(a[h[i]]) : s), (i==n?ORS:OFS) }
如果将以上内容存储在文件中merge.awk
你可以使用命令:
awk -f merge.awk f1 f2 f3 f4 ... fx
类似的方式,但不那么麻烦f
:
BEGIN { s="-" } # define symbol
BEGIN { # modify argument list from
c=ARGC; # from: arg1 arg2 ... argx
ARGV[ARGC++]="f=1" # to: arg1 arg2 ... argx f=1 arg1 arg2 ... argx
for(i=1;i<c;++i) ARGV[ARGC++]=ARGV[i]
}
!f { for (i=1;i<=NF;++i) h[$i] # read headers in associative array h[key]
nextfile
}
(f==1) && (FNR==1) { # process merged header
n=asorti(h) # sort header into h[idx] = key
for (i=1;i<=n;++i) # print header
printf "%s%s", h[i], (i==n?ORS:OFS)
f=2
}
# Start of processing the files
(FNR==1) { delete a; for(i=1;i<=NF;++i) a[$i]=i; next } # read header
{ for(i=1;i<=n;++i) printf "%s%s", (h[i] in a ? $(a[h[i]]) : s), (i==n?ORS:OFS) }
此方法略有不同,但允许将具有不同字段分隔符的文件处理为
awk -f merge.awk f1 FS="," f2 f3 FS="|" f4 ... fx
如果你的参数列表变得太长,你可以使用awk
为您创建它:
BEGIN { s="-" } # define symbol
BEGIN { # read argument list from input file:
fname=(ARGC==1 ? "-" : ARGV[1])
ARGC=1 # from: filelist or /dev/stdin
while ((getline < fname) > 0) # to: arg1 arg2 ... argx
ARGV[ARGC++]=$0
}
BEGIN { # modify argument list from
c=ARGC; # from: arg1 arg2 ... argx
ARGV[ARGC++]="f=1" # to: arg1 arg2 ... argx f=1 arg1 arg2 ... argx
for(i=1;i<c;++i) ARGV[ARGC++]=ARGV[i]
}
!f { for (i=1;i<=NF;++i) h[$i] # read headers in associative array h[key]
nextfile
}
(f==1) && (FNR==1) { # process merged header
n=asorti(h) # sort header into h[idx] = key
for (i=1;i<=n;++i) # print header
printf "%s%s", h[i], (i==n?ORS:OFS)
f=2
}
# Start of processing the files
(FNR==1) { delete a; for(i=1;i<=NF;++i) a[$i]=i; next } # read header
{ for(i=1;i<=n;++i) printf "%s%s", (h[i] in a ? $(a[h[i]]) : s), (i==n?ORS:OFS) }
可以运行为:
$ awk -f merge.awk filelist
$ find . | awk -f merge.awk "-"
$ find . | awk -f merge.awk
或任何类似的命令。
正如您所看到的,通过仅添加一小部分代码,我们就能够灵活地调整 awk 代码来支持我们的需求。