2013-03-02 6 views
10

Предположим, что у меня есть два файла, en.csv и sp.csv, каждая из которых содержит ровно две разделенные запятыми записей:Как получить все поля во внешнем соединении с Unix?

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?


фон и детали

У меня есть оболочка Unix (ЗШ) скрипт, который предназначен для процессов несколько flatfiles CSV, и делает это путем обширного использования GNU join 's' - o auto '. Мне нужно изменить этот сценарий, чтобы он мог работать в средах, где доступная команда 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 "[email protected]") 
    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 и столбец 3 из de.csv один будет работать:

% _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 
+1

имея делать то, что вы говорите на 1-офф проекта с sun4, я думаю, что вы «застрял в кодировании своей собственной или поставлял новое GNU-соединение как часть вашей установки. Извините, но удачи. – shellter

+1

Полагаю, я должен добавить, после долгих беспорядков, я закончил делать вспомогательные массивы в awk с гораздо меньшими проблемами. Удачи. – shellter

ответ

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 

F там легко вырезать интересующие столбцы и просто печатать их бок о бок, используя paste.

Для достижения этой цели мы сортируем входные файлы первого, а затем применить какую-то дурацкую awk магии:

  • Если запись об их ожидаемом номере строки появляется, распечатать его
  • В противном случае, печать, как многий строки, содержащие количество ожидаемых (это зависит от количества полей первой строки в файле, то же самое, что и join -o auto) MISSING полей до выравнивания снова верны
  • Не все входные файлы собираются такое же количество записей, поэтому максимальный поиск выполняется до всего этого. Затем печатаются больше строк с полями MISSING до тех пор, пока не будет достигнут максимум.

Код

reccut.sh:

#!/bin/bash 

get_max_recnum() 
{ 
    awk -F, '{ if ($1 > max) { max = $1 } } END { print max }' "[email protected]" 
} 

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=([email protected]) 
    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 "[email protected]") 
    else 
     align_by_recnum "${infile}" "${MAXREC}" | cut -d, -f ${cols} 
    fi 
} 

_reccut "[email protected]" 

Run

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

Было бы хорошо только получить отклик на это, это сработает для вас? –