объединение двух файлов с разделителями табуляцией по столбцу с одинаковыми идентификаторами в ОДНОМ шаге (команда)? - PullRequest
1 голос
/ 01 октября 2010

Очень часто я хочу объединить два ascii-файла, которые являются таблицами в том смысле, что они состоят из столбцов, разделенных табуляцией, например:

файл 1

FRUIT   ID
apple   alpha
banana  beta
cherry  gamma

файл 2

ID  FOOBAR
alpha   cat
beta    dog
delta   airplane

, и я хочу присоединиться к ним следующим образом: внутреннее соединение:

FRUIT   ID  FOOBAR
apple   alpha   cat
banana  beta    dog

или левое соединение:

FRUIT   ID  FOOBAR
apple   alpha   cat
banana  beta    dog
cherry  gamma   n/a

(идентификаторы, используемые для объединения, не обязательно являются уникальными.)

Пока что я занимаюсь:

  1. Копирование входных файлов без заголовка.
  2. Сортировкавходные файлы по столбцам.
  3. Используйте команду linux join в отсортированных версиях.
  4. Удалите промежуточные файлы.

Это подвержено ошибкам, так как мне нужно сосчитатьстолбцы, чтобы указать их позже для «сортировки» и «объединения» по номеру (еще более подвержен ошибкам с большим количеством столбцов и очень широких столбцов), я не должен забывать указывать, что вкладка является разделителем и необходимо удалить / вставить / исправитьзаголовок каждый раз и т. д.

Кто-нибудь может порекомендовать более простой способ?Предпочтительно, где мне не нужно сортировать и где я могу указать столбец по имени, а не по номеру?Что-то вроде «идентификатора соединения file1 file2> result»?

Ответы [ 2 ]

1 голос
/ 08 октября 2010

Совершенно другой подход заключается в использовании облегченного инструмента SQL, например sqlite.

Вы можете создать две таблицы:

$ sqlite3
SQLite version 3.7.2 
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table fruit (fruit varchar(20), id varchar(20));
sqlite> create table foobar (id varchar(20), foobar varchar(20));

установите TAB в качестве разделителя и загрузите ваши файлы:

sqlite> .separator "\t"
sqlite> .import file1 fruit
sqlite> .import file2 foobar

удалить заголовки:

sqlite> delete from fruit where id = 'ID';
sqlite> delete from foobar where id = 'ID';

затем выполните все необходимые запросы:

sqlite> select fruit.id, fruit, foobar from fruit, foobar where fruit.id = foobar.id;
alpha   apple   cat
beta    banana  dog
sqlite> .quit
$ 

Также можно автоматизировать задачу с помощью bash здесь:

#!/bin/bash

sqlite3 <<-EOF
        create table fruit (fruit varchar(20), id varchar(20));
        create table foobar (id varchar(20), foobar varchar(20));
        .separator "\t"
        .import file1 fruit
        .import file2 foobar
        delete from fruit where id = 'ID';
        delete from foobar where id = 'ID';
        select fruit.id, fruit, foobar from fruit, foobar where fruit.id = foobar.id;
        .quit
EOF
0 голосов
/ 07 октября 2010

Вы можете автоматизировать свою задачу с помощью bash-скрипта и без использования временных файлов, как в этом примере:

#!/bin/bash

id="$1"
file1="$2"
file2="$3"

# get a filename as a parameter
# read first line of file to get $id position
get_pos() {
  awk -v id="$id" '{
      for (i = 1; i <= NF; i++)
        if ($i == id) {
          print i
          exit
        }
    }' "$1"
}

# get $id positions from headers of the two files
pos1=$(get_pos "$file1")
pos2=$(get_pos "$file2")

# print header
printf "%s\t" "$id"
head -n1 "$file1" | sed -r "s/$id(\t|$)//" | tr -d '\n'
head -n1 "$file2" | sed -r "s/$id(\t|$)//"

# print data, add -a1 option for left join
join -t$'\t' -1 $pos1 -2 $pos2 \
  <(tail -n+2 "$file1" | sort) \
  <(tail -n+2 "$file2" | sort)

Это не делает никакой проверки ошибок, и, возможно, может быть реализовано с другими инструментами, такими как python, более элегантным способом, но я надеюсь, что это поможет.

...