unix 多个文件的完全外部连接

taor4pac  于 2023-10-18  发布在  Unix
关注(0)|答案(2)|浏览(123)

有没有一个好的方法来执行'完全外部连接'多个文件在Unix上理想地使用GNU核心utils。我的文件是由uniq -c生成的,下面是一个可以生成的模拟示例:

echo "12 aa\n3 bb" > file1
echo "5 aa\n6 bb\n1 cc" > file2
echo "11 aa\n7 bb\n4 dd" > file3

看起来像:

tail -n +1 file*
==> file1 <==
12 aa
3 bb

==> file2 <==
5 aa
6 bb
1 cc

==> file3 <==
11 aa
7 bb
4 dd

我想一个接一个地合并它们,使用序列(第2列)作为键,如果键不包含(外部连接),则填充0。也就是说,期望的输出看起来像这样

12 5 11 aa
3 6 6 bb
0 1 0 cc
0 0 4 dd

到目前为止,我发现join至少可以完成成对合并的工作:

join -j2 file1 file2 -a 2 -a 2 -e '0' -o '1.1,2.1,0' > merged
# 12 5 aa
# 3 6 bb
# 0 1 cc

注意:j2:查看第二列的key(对于两个文件)-a FILENUM:我还可以打印文件FILENUM中不可配对的行,其中FILENUM为1或2,对应于文件1或文件2
但我不知道如何将其推广到多个文件,即。这一个不工作,这意味着我不能很容易地把它放在一个循环中:

join -j2 merged file3 -a 2 -a 2 -e '0' -o '1.1,2.1,0' > merged2

我最好不要使用SQL来实现这一点,但如果没有其他方法也可以。

oipij1gg

oipij1gg1#

如果你有时间学习如何使用grepsedawk,你可以做你想做的事情。
否则,您可以使用此低效解决方案:

printf '12 aa\n3 bb\n' > file1
printf '5 aa\n6 bb\n1 cc\n' > file2
printf '11 aa\n7 bb\n4 dd\n' > file3
printf 'file1\nfile2\nfile3\n' > files

cat files | while read fil; do
    sed 's/^[0-9]* //' "$fil"
done | sort | uniq > lines

cat lines | while read line; do
    cat files | while read fil; do
        num=$(grep "$line" "$fil" | sed -E 's/^([0-9]+)[^0-9].*$/\1/')
        if [ "$num" = "" ]; then
            printf '0 '
        else
            printf '%s ' "$num"
        fi
    done
    printf '%s\n' "$line"
done > result

cat result

此脚本中使用的基本工具是shell的read内置命令。有了它,你可以parse a file line by line
原则是:对于每个“行”(aa,bb,cc,dd),使用grep获取该行在每个文件(file1,file2,file3)中出现的次数。有了这些信息,你就可以生产出你想要的产品。

0ve6wy6x

0ve6wy6x2#

最后,使用以下bash脚本multi_join_from_uniq.sh打包的sort找到了一个有效的解决方案:

#!/bin/sh

join_rec() {
  file1=$1
  file2=$2
  shift 2
  if [ $# -gt 0 ]; then
    join -e0 -a 1 -a 2 "$file1" -o auto "$file2" | join_rec - "$@"
  else
    join -e0 -a 1 -a 2 "$file1" -o auto "$file2"
  fi
}

join_rec "$@"

然而,该脚本仅适用于已排序和已交换的列,这可以作为进程替换来完成,以使此示例工作:

sh multi_join_from_uniq.sh <(awk '{print $2,$1}' file1 | sort) <(awk '{print $2,$1}' file2 | sort ) <(awk '{print $2,$1}' file3 | sort )

产生以下输出:

aa 12 5 11
bb 3 6 7
cc 0 1 0
dd 0 0 4

当然,这也可以在管道传输uniq命令的输出时完成,例如。这样cat data.txt | uniq -c | awk '{print $2,$1}' | sort > file1左右。

相关问题