Awk объединяет несколько строк условно - PullRequest
6 голосов
/ 15 августа 2011

Я хочу объединить значения из нескольких строк различной длины в одну строку, если они соответствуют идентификаторам.

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

ID:  Value:
a-1  49
a-2  75
b-1  120
b-2  150
b-3  211
c-1  289
d-1  301
d-2  322

Пример требуемого вывода:

ID:  Value:
a 49,75
b 120,150,211
c 289
d 301,322

Как мне написать выражение awk (или sed, grep или что-то) для проверкиесли идентификаторы совпадают, а затем вывести все эти значения в одну строку?Конечно, я могу просто напечатать их в разные столбцы и объединить их позже, поэтому на самом деле проблема заключается только в условной печати, если идентификаторы совпадают, и если не начинается новая строка.

Ответы [ 5 ]

5 голосов
/ 15 августа 2011

В awk, если ваши идентификаторы сгруппированы вместе:

awk 'NR==1 {print $0}
NR > 1 {sub("-.*", "", $1)}
NR == 2 {prev=$1; printf "%s %s", $1, $2}
NR > 2 && prev == $1 {printf ",%s", $2}
NR > 2 && prev != $1 {prev=$1; printf "\n%s %s", $1, $2}' your_input_file
3 голосов
/ 15 августа 2011

В sed, при условии, что идентификаторы объединены в кластеры:

sed -n -e '1p;2{s/-.* / /;h};3,${H;x;s/\(.*\) \(.*\)\n\1-.* /\1 \2,/;/\n/{P;s/.*\n//;s/-.* / /};x};${x;p}' your_input_file

Ниже приводится файл сценария sed с комментариями, который можно запустить с помощью sed -n -f script your_input_file:

# Print the 1st line as is.
1p
# For the 2nd line, remove what is after - in the ID and save in the hold space.
2{s/-.* / /;h}
# For all the other lines...
3,${
# Append the line to the hold space and place it in the pattern space.
H;x
# Substitute identical ids by a ,.
s/\(.*\) \(.*\)\n\1-.* /\1 \2,/
# If we have a \n left in the pattern space, it is a new ID, so print the old and prepare the next.
/\n/{P;s/.*\n//;s/-.* / /}
# Save what remains in hold space for next line.
x}
# For the last line, print what is left in the hold space.
${x;p}
3 голосов
/ 15 августа 2011

С учетом вашего ввода:

awk '
  NR == 1 {print; next}
  {
    split($1,a,/-/)
    sep = values[a[1]] == "" ? "" : ","
    values[a[1]] = values[a[1]] sep $2
  }
  END {for (key in values) print key, values[key]}
'

производит

ID:  Value:
a 49,75
b 120,150,211
c 289
d 301,322

Язык, который поддерживает "hash-of-lists", также был бы удобен. Вот версия Perl

perl -lne '
  if ($. == 1) {print; next}
  if (/^(.+?)-\S+\s+(.*)/) {
    push @{$values{$1}}, $2;
  }
  END {
    $, = " ";
    foreach $key (keys %values) {
    print $key, join(",", @{$values{$key}});
    }
  }
'
1 голос
/ 18 августа 2011

Учитывая ваши входные данные в файле input.txt:

awk '{split($1, a, "-"); hsh[a[1]]=hsh[a[1]]$2","}END{for (i in hsh){print i" "hsh[i]}}' input.txt | sed 's/,$//'

OUTPUT

a 49,75
b 120,150,211
c 289
d 301,322
0 голосов
/ 20 августа 2011

Решение, основанное на стандартных инструментах, как альтернатива превосходным решениям, представленным выше ...

$ for INDEX in $(cut -f1 input | uniq); do echo -n "$INDEX  ";grep "^$INDEX" input | cut -f2 | tr '\n' ' ';echo; done
a  49 75 
b  120 150 211 
c  289 
d  301 322 

Использование слегка измененного ввода без заголовка и индекса, созданного с использованием

awk 'NR>1' input | sed 's/-[0-9]*//'
a       49
a       75
b       120
b       150
b       211
c       289
d       301
d       322
...