2011-07-02 2 views
3

Скажем, у меня есть куча файлов XML, которые не содержат новой строки, но в основном содержат длинный список записей, ограниченный </record><record>труба одна длинная линия, как несколько строк

Если разделителем были </record>\n<record> я мог бы сделать что-то вроде cat *.xml | grep xyz | wc -l для подсчета экземпляров интересующих записей, потому что кошка будет выводить записи по одному в каждой строке.

Есть ли способ написать SOMETHING *.xml | grep xyz | wc -l, где SOMETHING может транслировать записи по одной в строке? Я попытался использовать awk для этого, но не смог найти способ избежать потоковой передачи всего файла в память.

Будем надеяться, что вопрос достаточно ясно :)

ответ

5

Это немного некрасиво, но это работает: (. Да, я знаю, что я мог бы сделать его немного короче, но только за счет ясности)

sed 's|</record>|</record>\ 
|g' *.xml | grep xyz | wc -l 

+1

Это работает очень хорошо, намного быстрее, чем моя версия 'awk', но чтобы быть понятным, это заставляет' sed' читать весь файл в памяти, правильно? Есть ли более «потоковая» версия? Что делать, если XML-файл представляет собой гигантский размер? – nicolaskruchten

+2

@nicolas: Вы можете использовать Perl и функцию 'read' (а не' '> оператор), чтобы читать фрагменты файла, вставлять новые строки и записывать их. Единственным сложным битом является то, что если Block 13 окажется неудачным, чтобы закончить часть пути через последовательность ' ', вы сохраняете конечный контекст для обработки со следующей частью. Обратите внимание, в частности, что если блок 13 заканчивается символом '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' Я не думаю, что есть стандартная утилита для такого рода расщепления; большинство утилит Unix работают на линиях. –

+0

немного читаем: 'sed $ 's | | & \ n | g '... '- использует конструкцию bash [' $' ''] (http://www.gnu.org/software/bash/manual/bashref.html#ANSI_002dC-Quoting) для включения escape-последовательностей в строках. –

2

Если запись тело не имеет характера, как < или / или >, то вы можете попробовать это:

grep -E -o 'SEARCH_STRING[^<]*</record>' *.xml| wc -l 

или

grep -E -o 'SEARCH_STRING[^/]*/record>' *.xml| wc -l 

или

grep -E -o 'SEARCH_STRING[^>]*>' *.xml| wc -l 
+0

ОК, это очень приятно, и отвечает ли мой конкретный вопрос о оглавлению по записям, но я искал более общее решение, чтобы разваливается длинную линию и излучающий записи в трубку по одному, a-la версия 'sed' в другом ответе :) Все еще ищете что-то, что не загружает все это в память в первую очередь! – nicolaskruchten

+0

Кроме того, версия 'sed' в другом ответе намного быстрее, я не знаю, почему ... – nicolaskruchten

2

Вот другой подход с использованием xsltproc, grep и wc. Предупреждение: я новичок в XSL, поэтому я могу быть опасным :-). Вот мой count_records.xsl файл:

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="text" />  <!-- Output text, not XML --> 
    <xsl:template match="record">  <!-- Search for "record" node --> 
    <xsl:value-of select="text()"/> <!-- Output: contents of node record --> 
    <xsl:text>      <!-- Output: a new line --> 
    </xsl:text> 
    </xsl:template> 

</xsl:stylesheet> 

На моем Mac, я нашел инструмент командной строки под названием xsltproc, который гласил инструкцию из файла XSL, файлов XML процесса. Таким образом, команда будет:

xsltproc count_records.xsl *.xml | grep SEARCH_STRING | wc -l 
  • В xsltproc команда отображает текст в каждом узле, одна строка в то время
  • В GREP команда отфильтровывает текст, который вы заинтересованы в
  • Наконец, команда туалет производит подсчет
1

Вы можете РБП попробовать xmlstarlet для кабриолета размера файлов:

# cf. http://niftybits.wordpress.com/2008/03/27/working-with-huge-xml-files-tools-of-the-trade/ 

xmlstarlet sel -T -t -v "count(//record[contains(normalize-space(text()),'xyz')])" -n *.xml | 
    awk '{n+=$1} END {print n}' 

xmlstarlet sel -T -t -v "count(//record[contains(normalize-space(text()),'xyz')])" -n *.xml | 
    paste -s -d '+' /dev/stdin | bc