11

假设我有两个文件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

这种产生完全外连接的方法的一个缺点是需要显式指定最终表的格式,这在编程应用程序中可能不容易做到(连接表的身份仅在运行时才知道)。

最新版本的 GNUjoin通过支持特殊格式消除了这个缺点auto。因此,使用join上面最后一个命令的这样一个版本,可以用更通用的命令代替

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

如何使用join不支持该-o auto选项的版本实现相同的效果?


背景和细节

我有一个 Unix shell (zsh) 脚本,旨在处理多个 CSV 平面文件,并通过广泛使用 GNUjoin的 '-o auto' 选项来实现。我需要修改此脚本,以便它可以在可用join命令不支持该-o auto选项的环境中工作(例如 BSDjoin以及旧版本的 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

然后,例如,要并排显示 的第 3en.csv列、第 2 列和第 4sp.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
4

1 回答 1

2

这是一个可能适用于您的数据的解决方案,也可能不适用于您的数据。它通过按行号对齐 csv 文件中的记录来解决问题,即记录2在 line 上2,record 3123on line number3123等等。缺少的记录/行用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
于 2013-04-28T12:02:15.070 回答