2015-08-21 2 views
2

В настоящее время у меня есть команда, которая выводит данные в следующем формате:Объединить и манипулировать древовидную структуру в Баш

apple: banana 
apple: cantaloupe 
apple: durian 
apple: eggplant 
banana: cantaloupe 
banana: durian 
durian: eggplant 
eggplant: 

Другими словами, это древовидная структура, в которой apple является корнем, которое имеет детей banana и eggplant, и banana также имеет детей младшего возраста cantaloupe и durian. eggplant не имеет детей, но все еще имеет заднюю кишку.

Я хочу, чтобы сцепить выход в этот формат:

apple: banana eggplant 
banana: cantaloupe durian 
durian: eggplant 
eggplant: 

Некоторые объекты могут появиться более чем один раз на выходе (в данном случае, cantaloupe, durian и eggplant имеют несколько родительских узлов). В то время как этот пример не имеет этого, также могут быть несколько корневых узлов (т. Е. Такой же ширины, как apple).

Как я могу изменить этот выход? Я использую bash/shell-скрипты в целом прямо сейчас, поэтому я думал, что awk, вероятно, будет лучшим способом справиться с этим, но если это лучше обрабатывается на Python, Ruby, Perl или на каком-то другом языке сценариев, я также открыт для предложений.

+0

Ваше описание «tree- как «структура», скорее звучит как DAG (Directized acyclic graph). Инструмент 'make' использует эти внутренние функции, а' tsort' из стандартного инструментария unix может выполнять топологическую сортировку для вас. – liborm

+0

Вы знаете, это забавно, что вы вызываете 'make', потому что я на самом деле пытаюсь построить дерево зависимостей' make' целей! Результат ниже работает для меня, хотя, но спасибо за подсказку на 'tsort' - никогда не слышал об этом раньше. –

+1

Эй, честно говоря, это вопрос хамелеона! Задайте вопрос, на который вы хотите ответить. Не изменяйте его постепенно. Лучше спросить новый вопрос, если вы обнаружите, что на ваш оригинал был дан ответ, но на самом деле вы этого не хотели. –

ответ

2

Вы можете использовать AWK:

awk -F ': *' '{a[$1] = (a[$1]? a[$1] OFS $2 : $2)} 
     END { for (i in a) print i ": " a[i] }' file 
eggplant: 
apple: banana cantaloupe durian eggplant 
banana: cantaloupe durian 
durian: eggplant 

Чтобы сохранить первоначальный порядок:

awk -F ': *' '!($1 in a){b[++n]=$1} {a[$1] = (a[$1]? a[$1] OFS $2 : $2)} 
    END{for (i=1; i<=n; i++) print b[i] ": " a[b[i]]}' file 
apple: banana cantaloupe durian eggplant 
banana: cantaloupe durian 
durian: eggplant 
eggplant: 
+1

Красивая, спасибо! Я попробовал это на гораздо большем наборе данных и получил ожидаемые результаты. –

+1

По существу тот же ответ, что и мой.Вы использовали разделитель полей fancier, но вам нужно сделать больше работы, чтобы сохранить интервал. Я использовал более простой разделитель полей и просто должен был избегать добавления пробелов. Один недостаток для моего: если данные не отстоят друг от друга, как показано (с пробелом после двоеточия), тогда имена будут конкатенированы на выходе. Вы можете упростить свое использование, просто добавив в список пространство и новое поле; вам тогда не нужно пробел после двоеточия в выходе. –

+0

Правильно, немного короче будет: 'awk -F: '! ($ 1 in a) {b [++ n] = $ 1} {a [$ 1] = a [$ 1] $ 2} END {for (i = 1; i <= n; i ++) print b [i] FS a [b [i]]} 'file' – anubhava

2
awk -F: '{ list[$1] = list[$1] $2 } END { for (i in list) printf "%s:%s\n", i, list[i] }' 

аккумулировать записи с использованием ассоциативных массивов в awk, построения списка. Конкатенация строк в awk немного странно. В конце распечатайте ключи и записи для ключа. Если вам требуется заказ, вам нужно это сказать.

Предполагая, что клавиши на левой стороне должны быть выведены в порядке первого появления на LHS входа, то вы можете использовать этот несколько более сложный сценарий:

awk -F: '{ if (!($1 in list)) keys[++n] = $1; list[$1] = list[$1] $2 } 
     END { for (j = 1; j <= n; j++) printf "%s:%s\n", keys[j], list[keys[j]] }' 
+0

Вы можете предположить, что вход упорядочен, так как я могу «сортировать» его, когда он выходит. –

Смежные вопросы