sed первый n набор результатов случаев между 2 тегами / шаблоны

GNU head и tail с тех пор coreutils версия 8.25 имеют a -z опция для этого.

С более старыми версиями или для систем не-GNU, можно попытаться подкачать \0 и \n:

find ... -print0 |
  tr '\0\n' '\n\0' |
  head |
  tr '\0\n' '\n\0'

Обратите внимание что некоторые head реализации не могут справиться с символами NUL (и они не обязаны POSIX), но где находят поддержки -print0, head и текстовые утилиты обычно поддерживают символы NUL.

Можно также использовать функцию для обертывания любой команды между двумя trs:

nul_terminated() {
  tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}

find ... -print0 | nul_terminated tail -n 12 | xargs -r0 ...

Следует иметь в виду это под nul_terminated, a \0 означает символ новой строки. Так, например, для замены \n с _:

find . -depth -name $'*\n*' -print0 | nul_terminated sed '
  p;h;s,.*/,,;s/\x0/_/g;H;g;s,[^/]*\n,,' | xargs -r0n2 mv

(\x0 будучи также расширением GNU).

Если необходимо выполнить больше чем одну команду фильтрации, можно сделать:

find ... -print0 |
  nul_terminated cmd1 |
  nul_terminated cmd2 | xargs -r0 ...

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

find ... -print0 | nul_terminated eval 'cmd1 | cmd2' | xargs -r0 ...
5
03.06.2014, 18:09
6 ответов

Вам действительно следует использовать для этого парсер, но, чтобы вы знали, sed -n '/ /, / <\ / tag> / p 'file.xml дает вам все элементы, потому что вы p их все списываете. Эта команда работает, обращаясь ко всем строкам между строкой, содержащей , и следующей строкой ввода, содержащей . Так как это делает почти все ваши строки, простая их промывка p не покажет большой разницы. Что-то вроде следующего может быть немного ближе к метке:

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}'

Он обращается к строкам и проверяет их на наличие . Если они не содержат закрывающую строку, он втягивает другую строку - и это повторяется до тех пор, пока пространство шаблонов не будет содержать . * [^ \ n] * $ .

Затем я просто перевожу все \ n символы ewline в пространстве шаблонов в пробелы.

Вот оно снова:

sed -n '\|<tag>|{:n;\|</tag>|!{N;bn};y|\n| |;p}' <<\DATA
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
</root>
DATA

ВЫВОД:

<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>

Теперь вы можете сделать:

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}' ./file | 
sed 's|> |>\n|g;2q'

... что меня поймает:

<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
3
27.01.2020, 20:35

Труба через головку - N . После первых N вхождений head завершает работу и, таким образом, sed останавливается.

0
27.01.2020, 20:35

Насколько я знаю, совпадения sed всегда жадные, т.е. / /, / <\ / tag > / будет соответствовать от первого экземпляра до последнего экземпляра <\ tag> - включая любые другие объекты XML между ними.

Если ваша версия awk поддерживает многосимвольные разделители записей, вы можете сделать что-то вроде

awk -v n=2 'BEGIN{RS="</tag>\n";ORS=RS} NR<=n'

, но на самом деле более надежным решением будет использование выделенного синтаксического анализатора XML - например, очень минимальная реализация с использованием python minidom

#!/usr/bin/python

from xml.dom import minidom

xmldoc = minidom.parse('file.xml')
taglist = xmldoc.getElementsByTagName('tag')
for i in range(2) :
        print taglist[i].toxml()
0
27.01.2020, 20:35

Я думаю, это то, что вам нужно,

sed -n '/<tag>/,/<\/tag>/p' file.xml | head -10

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

$ sed -n '/^<tag>/p' file.xml | head -2
<tag><t1>john</t1></tag>
<tag><t1>john</t1></tag>
1
27.01.2020, 20:35

Что ж, в конце концов я отвечу себе на свой вопрос.

Решение, которое я нашел, работает в 2 (может быть, 3) шагах:

1 - Получение всех требуемых элементов с помощью:

sed -n '/<tag>/,/<\/tag>/p' file.xml > selectedItems.xml

2 - Получение N-й позиции последнего элемента с помощью

POS = grep -n '</tag>' ./selectedItems.xml | head -n [POS] | tail -n 1

3 - Получение первых N требуемых элементов:

sed -n 1,[POS]p selectedItems.xml > selectedItems.xml

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

PS Чтобы убедиться, что позиция соответствует реальной N-й позиции в дереве (когда XML-файл формируется полностью в строке), я использовал:

xmllint --format ./myxmlfile.xml
0
27.01.2020, 20:35

Попробуйте:

xmllint --xpath '//tag[position()<=2]' file.xml

Или:

xmlstarlet sel -t -c '//tag[position()<=2]' file.xml

Или:

xmlstarlet sel -t -m '//tag[position()<=2]' -c . -n file.xml

Если бы вы хотели сделать это с помощью sed , только вы могли сделайте что-нибудь вроде:

sed -n '
  1{x;s/^/../;x;}; # initialise counter with two tokens
  /<tag>/,/<\/tag>/ {
    p; /<\/tag>/{
      x;s/.//;/./!q;x; # remove a token and quit if hold space empty
    }
  }' file.xml

То есть используйте удерживаемое пространство в качестве счетчика оставшихся отображаемых разделов (используя символы точки).

5
27.01.2020, 20:35

Теги

Похожие вопросы