Sed между двумя шаблонами, где результат содержит третий шаблон - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь отфильтровать файлы журнала для ответов xml, и используя sed, как показано ниже, довольно легко найти все сообщения xml.

sed -n '/<element/,/<\/element/p' file

Возвращает:

<element>
    <id>12345</id>
    ...
</element>
<element>
    <id>54321</id>
    ...
</element>

Однако мне не удалось выяснить, как применить второй фильтр, что означает, что возвращаются только ответы XML, содержащие определенный шаблон, такой как идентификатор.

В приведенном выше примере, как я могу отфильтровать по идентификатору, чтобы вернуть только первый?

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

sed - для выполнения s / old / new / ЭТО ВСЕ . Все его причудливые односимвольные рунические языковые конструкции устарели в середине 1980-х годов, когда был изобретен awk.

$ cat tst.awk
/<element>/ { inElt = 1 }
inElt {
    elt = (elt == "" ? "" : elt ORS) $0
    if ( /<\/element>/ ) {
        if ( elt ~ /<id>12345<\/id>/ ) {
            print elt
        }
        elt = ""
        inElt = 0
    }
    next
}
{ print }

$ awk -f tst.awk file
<element>
    <id>12345</id>
    ...
</element>

Основными преимуществами вышеперечисленного по сравнению с принятым в настоящее время решением sed являются:

  1. Не требуется дважды тестировать для </element
  2. Он будет работать так же, как при использовании любого awk в любой оболочке на любом компьютере UNIX, а не только с некоторыми seds
  3. Можно тривиально улучшить добавление дополнительных и / или различных условий для печати (или нет) связанного элемента
  4. Он не зависит от каких-либо загадочных односимвольных командных символов, все четко изложено с использованием синтаксиса на основе алфавита, общего для многих современных языков.

Например, допустим, вы хотите напечатать первый элемент в файле независимо от его идентификатора, а не тот, который содержит конкретный идентификатор. Это было бы тривиальным твиком:

$ cat tst.awk
/<element>/ { inElt = 1 }
inElt {
    elt = (elt == "" ? "" : elt ORS) $0
    if ( /<\/element>/ ) {
        if ( ++cnt == 1 ) {
            print elt
        }
        elt = ""
        inElt = 0
    }
    next
}
{ print }

$ awk -f tst.awk file
<element>
    <id>12345</id>
    ...
</element>

Если вы хотите напечатать 27-й вместо 1-го элемента, просто измените ++cnt == 1 на ++cnt == 27. Попробуйте изменить скрипт sed для такого тривиального изменения требований, и вы можете рассчитывать на полное переписывание и необходимость вызова дополнительных инструментов. Хотите напечатать несколько элементов и / или других частей файла, не входящих в теги элементов? Также абсолютно тривиально с awk. Надеюсь, вы поняли.

0 голосов
/ 04 июля 2018

Это может сработать для вас (GNU sed):

sed -n '/<element>/{:a;/<\/element>/!{N;ba};/<id>12345<\/id>/p}' file

Используйте seds grep-like nature, используя опцию -n, которая отключает автоматическую печать каждой строки. При обнаружении строки, содержащей <element>, соберите набор строк, пока не будет достигнут конечный тег </element>. Теперь проверьте коллекцию на <id>12345</id> и напечатайте коллекцию, если true, иначе коллекция передана.

Если вместо этого вы хотите определенный элемент, например второе, используйте:

sed -n '/<element>/{:a;/<\/element>/!{N;ba};x;s/^/x/;/^x\{2\}$/{x;p;b};x}' file

Используется счетчик, удерживаемый в области удержания, который увеличивается на каждую полную коллекцию и проверяется на конкретное число.

N.B. Оператор диапазона , может использоваться как команда типа триггера, но в целом start address{:a;N;end address!ba; commands on collection} более полезен.

0 голосов
/ 02 июля 2018

Вы можете группировать команды для своих диапазонов:

sed -n '/<element/,/<\/element/{ /id/p }'

Но вы должны рассмотреть возможность использования инструментов XML при работе с XML, таких как xmlstarlet .

Чтобы напечатать полную запись при поиске определенного идентификатора, вам нужно накопить строки внутри узла <element>, используя пробел , если вы достигли конечного тега <element> узел, вы можете заменить удерживать и пробелы , сопоставить свой идентификатор и распечатать его:

sed -n -e '
  /<element/,/<\/element/H # append to the hold space
  /<\/element/{ 
    g  # replace pattern space with hold space
    /<id>12345<\/id>/p  # print if matching ID
    s/.*//  # clear pattern space
    x  # clear hold space
    b  # start next cycle without further output
}' input-file

Видите ли, это очень быстро запутывается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...