Как соединить несколько строк, если шаблон соответствует многосегментному - PullRequest
0 голосов
/ 19 февраля 2019

Мой файл содержит:

segment  
bob is working  
linda is studying  
john is reading  
+b eating  
+b drinking  
+j listening  
segment  
john is driving  
linda is cooking  
bob is sleeping  
+b snoring  
+l washing  
segment  

Я хочу обработать файл с чем-то вроде sed, awk, или что-нибудь, чтобы стать:

segment  
bob is working eating drinking  
linda is studying  
john is reading listening  
segment  
john is driving  
linda is cooking washing  
bob is sleeping snoring  
segment  

Я пытался:

sed 'N;s/\n\s*+b//;P;D' file

Однако только строки с +b обработаны

Буду признателен за любую помощь.

Ответы [ 3 ]

0 голосов
/ 19 февраля 2019

Если perl - ваш вариант, попробуйте следующее:

perl -ne '
    s/\s+$//;
    if (/^segment/) {
        push(@ary, $_);
        print(join("\n", @ary), "\n");
        undef @ary;
    } elsif (/^(\S)\S*\s+is/) {
        push(@ary, $_);
        $index{$1} = $#ary;
    } elsif (/^\+(\S)\s+(\S+)/) {
        $ary[$index{$1}] .= " $2";
    }
' file.txt

output:

segment
bob is working eating drinking
linda is studying
john is reading listening
segment
john is driving
linda is cooking washing
bob is sleeping snoring
segment
  • -n Опция perl указывает на итерациювходной файл как awk -n.
  • s/\s+$// удаляет завершающие символы новой строки и пробелы, если таковые имеются.
  • Часть if (/^segment/) сбрасывает содержимое в @aryи сбросьте массив для следующего сегмента.
  • Следующая часть elsif (/^(\S)\S*\s+is/) совпадает со строкой, подобной bob is working, затем добавьте строку в @ary, запомнив индекс массива с начальным, как "b".
  • Следующая часть elsif (/^\+(\S)\s+(\S+)/) совпадает со строкой, подобной +b eating, затем добавьте действие eating к элементу @ary, проиндексированному с помощью" b ".

Я мог бы написать сценарий также с awk, но сценарий был бы длиннее.Я предпочитаю perl за его гибкость (и странность).
Надеюсь, это поможет.

0 голосов
/ 19 февраля 2019

Вероятно, не самая короткая, но вот простая версия sed:

sed <file -E '
    :l;
    /(^|\n)segment[ \t]*$/!{
        N;
        s/(^|\n)(.)([^\n]*)[ \t](.*)\n[+]\2[ \t]+([^\n]*)/\1\2\3\5\4/;
        bl;
    }
'
  • , если не совпадает с линией сегмента,
    • добавляет следующую строку к пробелу
    • поиск строк, начинающихся с x и + x, и добавление хвоста последнего к предыдущему
    • переход наверх
  • в противном случае, неявная печать, удаление пробела и запуск следующего цикла
0 голосов
/ 19 февраля 2019
$ cat tst.awk
{ sub(/[[:space:]]+$/,"") }
$0 == "segment" {
    for (keyNr=1; keyNr<=numKeys; keyNr++) {
        key = keys[keyNr]
        print line[key]
    }
    print
    numKeys = 0
    next
}
/^[+]/ {
    key = substr($0,2,1)
    line[key] = line[key] OFS $2
    next
}
{
    key = substr($0,1,1)
    line[key] = $0
    keys[++numKeys] = key
}

.

$ awk -f tst.awk file
segment
bob is working eating drinking
linda is studying
john is reading listening
segment
john is driving
linda is cooking washing
bob is sleeping snoring
segment
...