如何获得外各领域的Unix的加入加入?(How to get all fields in outer

2019-08-17 06:22发布

假设我有两个文件, en.csvsp.csv ,每个都包含正好两个逗号分隔的记录:

en.csv

1,dog,red,car
3,cat,white,boat

sp.csv

2,conejo,gris,tren
3,gato,blanco,bote

如果我执行

join -t, -a 1 -a 2 -e MISSING en.csv sp.csv

输出我得到的是:

1,dog,red,car
2,conejo,gris,tren
3,cat,white,boat,gato,blanco,bote

请注意,所有丢失的领域已经崩溃了。 为了得到一个“正确”的全外连接,我需要指定的格式; 从而

join -t, -a 1 -a 2 -e MISSING -o 0,1.2,1.3,1.4,2.2,2.3,2.4 en.csv sp.csv

产量

1,dog,red,car,MISSING,MISSING,MISSING
2,MISSING,MISSING,MISSING,conejo,gris,tren
3,cat,white,boat,gato,blanco,bote

这种方式产生完全外连接的一个缺点是一个需要明确指定了决赛桌,这可能不容易编程的应用(如连接表的身份只有在运行时是已知的)做的格式。

GNU的最新版本join所配套的特殊格式消除这种缺陷auto 。 因此,这样的版本join的最后一个命令上面可以由更一般的更换

join -t, -a 1 -a 2 -e MISSING -o auto en.csv sp.csv

我怎样才能实现与版本中,此相同的效果join不支持-o auto选项?


背景和细节

我有一个设计来处理几个CSV flatfiles,并广泛利用GNU的这样做Unix外壳(zsh中)脚本join的‘-o自动’选项。 我需要修改这个脚本,以便它可以在现有的环境中工作, join命令不支持-o auto选项(如对于BSD的情况下join以及旧版本的GNU的join )。

一个典型的使用脚本中的选项是这样的:

_reccut () {
    cols="1,$1"
    shift
    in=$1
    shift
    if (( $# > 0 )); then
        join -t, -a 1 -a 2 -e 'MISSING' -o auto \
          <( cut -d, -f $cols $in | sort -t, -k1 ) \
          <( _reccut "$@" )
    else
        cut -d, -f $cols $in | sort -t, -k1
    fi
}

我显示这个例子来说明,这将是难以取代-o auto有一个明确的格式,因为字段以这种格式不知道,直到运行时包括。

功能_reccut以上基本提取从文件列,并加入沿其第一列生成的表。 要了解如何_reccut在行动上,可以想象的是,除了上面提到的文件,我们也有文件

de.csv

2,Kaninchen,Grau,Zug
1,Hund,Rot,Auto

然后,例如,为了显示并排侧的3列en.csv ,列2和4 sp.csv ,和de.csv一个的柱3可以运行:

% _reccut 3 en.csv 2,4 sp.csv 3 de.csv | cut -d, 2-
red,MISSING,MISSING,Rot
MISSING,conejo,tren,Grau
white,gato,bote,MISSING

Answer 1:

下面是可能会或可能不会为您的数据工作的解决方案。 它由行号,即一个CSV文件中对齐记录接近问题记录2结束了上线2 ,记录3123上线人数3123等。 缺少记录/线填充为MISSING领域,所以输入文件将被错位看起来像这样:

en.csv

1,dog,red,car
2,MISSING,MISSING,MISSING
3,cat,white,boat

de.csv

1,Hund,Rot,Auto
2,Kaninchen,Grau,Zug
3,MISSING,MISSING,MISSING

sp.csv

1,MISSING,MISSING,MISSING
2,conejo,gris,tren
3,gato,blanco,bote

从那里,很容易切出感兴趣的列,只是使用打印出来并排侧paste

为了实现这一目标,我们输入文件第一排序,然后涂抹一些愚蠢的awk法宝:

  • 如果记录出现在他们的预期行数,打印
  • 否则,打印含有的预期数量多行(这是基于文件的第一行的领域,数量一样东西join -o auto一样) MISSING领域,直到对准再次正确
  • 并非所有的输入文件是要在相同数量的记录,所以最大的这一切之前搜索。 然后,更多的线与MISSING字段印刷直至最高命中。

reccut.sh

#!/bin/bash

get_max_recnum()
{
    awk -F, '{ if ($1 > max) { max = $1 } } END { print max }' "$@"
}

align_by_recnum()
{
    sort -t, -k1 "$1" \
        | awk -F, -v MAXREC="$2" '
            NR==1 { for(x = 1; x < NF; x++) missing = missing ",MISSING" }
            {
                i = NR
                if (NR < $1)
                {
                    while (i < $1)
                    {
                        print i++ missing
                    }
                    NR+=i
                }
            }1
            END { for(i++; i <= MAXREC; i++) { print i missing } }
            '
}

_reccut()
{
    local infiles=()
    local args=( $@ )
    for arg; do
        infiles+=( "$2" )
        shift 2
    done
    MAXREC="$(get_max_recnum "${infiles[@]}")" __reccut "${args[@]}"
}

__reccut()
{
    local cols="$1"
    local infile="$2"
    shift 2

    if (( $# > 0 )); then
        paste -d, \
            <(align_by_recnum "${infile}" "${MAXREC}" | cut -d, -f ${cols}) \
            <(__reccut "$@")
    else
        align_by_recnum "${infile}" "${MAXREC}" | cut -d, -f ${cols}
    fi
}

_reccut "$@"

$ ./reccut.sh 3 en.csv 2,4 sp.csv 3 de.csv
red,MISSING,MISSING,Rot
MISSING,conejo,tren,Grau
white,gato,bote,MISSING


文章来源: How to get all fields in outer join with Unix join?