Объединить строки по первой колонке с помощью awk или sed

12

Как я могу использовать awkв следующей ситуации?

Я хочу объединить строки, начинающиеся с одного столбца. Только первый столбец сохраняется после соединения (в данном случае aaa, www, hhh).

Файл может быть разделен пробелом или табуляцией.

Пример ввода:

aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL

Желаемый вывод:

aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL

Основанием для этого является то, что я хочу создать очень простую файловую базу данных, где первый столбец всегда является идентификатором сущности. Все строки на основе одного столбца идентификатора объединяются.

крошечный
источник
1
откуда взялась uuuстрока (на выходе)?
Саидн
Извините, это моя ошибка. Я отредактирую это.
крошечный

Ответы:

8

Чтобы получить первые столбцы в каждой строке с помощью awk, вы можете сделать следующее:

< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh

Это ваши ключи для остальных строк. Таким образом, вы можете создать хеш-таблицу, используя первый столбец в качестве ключа и второй столбец строки в качестве значения:

< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111

Чтобы получить всю оставшуюся часть строки, начиная со столбца 2, вам нужно собрать все столбцы:

< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL 
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc    NULL NULL NULL NULL 
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL 
binfalse
источник
Привет, да, это действительно нужно разбить на хеш-таблицы. Спасибо!
крошечный
2
@tiny - я предполагал, что порядок должен быть сохранен. Разве это не так (этот ответ создает порядок, соответствующий механизму хеширования, а не ваш первоначальный порядок)?
ire_and_curses
3

Кто-то может ответить на awk или sed, но версия на Python проста и может быть полезна для вас.

#!/usr/bin/env python

input_file = 'input.dat'
in_fh      = open(input_file, 'r')

input_order = []
seen        = {}
for line in in_fh:    
    # Remove the newline character...
    line = line[:-1]

    # Separate the first column from the rest of the line...
    key_col, sep, rest_of_line = line.partition(" ")
    rest_of_line = sep + rest_of_line  

    # If we've seen this key already, concatenate the line...
    if key_col in seen:
        seen[key_col] += rest_of_line
    # ...otherwise, record the ordering, and store the new info
    else:
        input_order.append(key_col)
        seen[key_col] = rest_of_line

in_fh.close()

# Dump the ordered output to stdout
for unique_col in input_order:
    print unique_col + seen[unique_col]
ire_and_curses
источник
Очень круто. С моим питоном нулевого опыта мне даже удалось отредактировать скрипт, который принимает первый аргумент в качестве имени входного файла :)
крошечный
2

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

touch outfile
while read; do
  join -a1 -a2 outfile <(echo $REPLY) > tmp
  mv tmp outfile
done < infile

Для повышения его эффективности может помочь сохранение outfileи tmpвиртуальный диск.

редактировать

Или без временных файлов:

out=""
while read; do
  out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile

echo "$out"
Тор
источник
2

А вот PERL с одним вкладышем:

$ perl -e 'my %h; while(<>){chomp; @a=split(/\s+/); $k=shift(@a); $h{$k}.=join(" ", @a) . " "; } map{$h{$_}=~s/\s*$//; print "$_ $h{$_}\n}keys(%hash);' infile
Тердон
источник