Awk объединение нескольких строк условно
Я хочу объединить значения из нескольких строк различной длины в одну строку, если они соответствуют идентификаторам.
пример ввода:
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 ответов
в awk, если ваши идентификаторы сгруппированы вместе:
awk 'NR==1 {print }
NR > 1 {sub("-.*", "", )}
NR == 2 {prev=; printf "%s %s", , }
NR > 2 && prev == {printf ",%s", }
NR > 2 && prev != {prev=; printf "\n%s %s", , }' your_input_file
учитывая ваш входной сигнал:
awk '
NR == 1 {print; next}
{
split(,a,/-/)
sep = values[a[1]] == "" ? "" : ","
values[a[1]] = values[a[1]] sep
}
END {for (key in values) print key, values[key]}
'
производит
ID: Value:
a 49,75
b 120,150,211
c 289
d 301,322
язык, который поддерживает "хэш-оф-списки" было бы удобно тоже. Вот версия Perl
perl -lne '
if ($. == 1) {print; next}
if (/^(.+?)-\S+\s+(.*)/) {
push @{$values{}}, ;
}
END {
$, = " ";
foreach $key (keys %values) {
print $key, join(",", @{$values{$key}});
}
}
'
В sed, предполагая, что идентификаторы сгруппированы вместе:
sed -n -e '1p;2{s/-.* / /;h};3,${H;x;s/\(.*\) \(.*\)\n-.* / ,/;/\n/{P;s/.*\n//;s/-.* / /};x};${x;p}' your_input_file
Bellow-это прокомментированный файл сценария 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-.* / ,/
# 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}
учитывая ваши входы во вход.текстовый файл:
awk '{split(, a, "-"); hsh[a[1]]=hsh[a[1]]","}END{for (i in hsh){print i" "hsh[i]}}' input.txt | sed 's/,$//'
выход
a 49,75
b 120,150,211
c 289
d 301,322
решение основанное на стандартных инструментах, как альтернатива к превосходным решениям обеспеченным выше...
$ 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