首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过awk合并具有N个公共列的多个文件,如果任何文件没有公用键,则希望将列值替换为0

通过awk合并具有N个公共列的多个文件,如果任何文件没有公用键,则希望将列值替换为0
EN

Unix & Linux用户
提问于 2022-08-02 02:33:24
回答 2查看 110关注 0票数 2

我希望根据公共列合并多个文件,并希望在任何文件都没有该公共列时添加0。例如,见下文:

a1.txt

代码语言:javascript
运行
复制
111,222,444,5.5
121,321,555,1.2

a2.txt

代码语言:javascript
运行
复制
111,222,444,7.8
333,321,555,4.5
311,555,222,1.1

a3.txt

代码语言:javascript
运行
复制
333,321,555,9.1
311,555,222,8.8
444,666,777,2.5

匹配应该与前3列的组合。

产出应如下:

代码语言:javascript
运行
复制
111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
333,321,555,0,4.5,9.1
311,555,222,0,1.1,8,8
444,666,777,0,0,2.5

在3个输入文件中,第4列的值是不同的,我想按顺序排列。和1.txt一样,输出文件中的值应该是第4列。6th值应位于输出文件的第5列,而a3.txt的值应位于输出文件的第6列。我试了下,但没有给我预期的结果。

代码语言:javascript
运行
复制
awk '{ a[$1 FS $2 FS $3 FS] = a[$1 FS $2 FS $3 FS] ( a[$1 FS $2 FS $3 FS] == "" ? "" : FS) $4 } END{ for (i in a){print i,a[i,0],a[i]} }' FS="," a1.txt a2.txt a3.txt

这样,我想对4、5或6个输入文件做同样的操作。有人能帮我吗?

EN

回答 2

Unix & Linux用户

回答已采纳

发布于 2022-08-02 13:38:14

将GNU awk用于数组和ARGIND:

代码语言:javascript
运行
复制
$ cat tst.awk
BEGIN { FS=OFS=SUBSEP="," }
{ vals[$1,$2,$3][ARGIND] = $NF }
END {
    for ( key in vals ) {
        printf "%s", key
        for ( i=1; i<=ARGIND; i++ ) {
            printf "%s%g", OFS, vals[key][i]
        }
        print ""
    }
}
代码语言:javascript
运行
复制
$ awk -f tst.awk *.txt
111,222,444,5.5,7.8,0
311,555,222,0,1.1,8.8
333,321,555,0,4.5,9.1
444,666,777,0,0,2.5
121,321,555,1.2,0,0

如果输出行的顺序很重要,那么就很容易调整。

票数 0
EN

Unix & Linux用户

发布于 2022-08-02 05:09:40

与任何awk同时也要在输出中保存记录的顺序:

代码语言:javascript
运行
复制
awk 'BEGIN{ SUBSEP=OFS=FS="," }
 FNR==1 && !reProccss{ fileNr++ }
 !reProccss{ keys[$1, $2, $3, fileNr]=$4; next }
  reProccss{ key=($1 OFS $2 OFS $3); recNr++
             for(i=1; i<=fileNr; i++)
                 if(seen[key]++

或者使用with join +shell将多个列作为键转换为单个键,然后使用类似于按第一列合并多个文件的join命令(因为join只使用单个列作为键)来生成我们想要的输出。

因此,我们将多个键列转换为一个列,方法是在前两个文件上使用特定字符(例如- (输入文件中不应该存在的字符)分隔它们,并输出到临时文件joined.tmp:

代码语言:javascript
运行
复制
join -t, -a1 -a2 -e 0 -o auto \
    <( joined.tmp

然后,我们使用一个shell循环来针对joined.tmp文件处理其余的文件(它每次运行都会更新到与第二个下一个文件连接);我们还跳过了我们已经处理过的witin循环中的前两个文件。

代码语言:javascript
运行
复制
for file in ./a*.txt; do
    [ "$file" = "./a1.txt" -o "$file" = "./a2.txt" ] && continue
    join -t, -a1 -a2 -e 0 -o auto \
        joined.tmp <(sort "$file" |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') >joined.tmp.1
    mv joined.tmp.1 joined.tmp
done

最后,将添加的-更改为原来的字符,。

代码语言:javascript
运行
复制
sed 's/-/,/g' joined.tmp > joined-final.csv

由于join要求对输入文件进行排序,输出记录的顺序将被更改:

代码语言:javascript
运行
复制
$ cat joined-final.csv
111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
311,555,222,0,1.1,8.8
333,321,555,0,4.5,9.1
444,666,777,0,0,2.5 
票数 2
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/712091

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档