Остановить grep после совпадения шаблона, а не всей строки? Файлы с длинной строкой / одной строкой - PullRequest
3 голосов
/ 06 мая 2011

Я использую grep, чтобы вернуть только строку между шаблоном (-o), в пределах ряда файлов, например,

grep -i -r -o 'Rows="[^#][^"]*"' *

Я бы хотел, чтобы он остановился после первого совпадения, я попробовал grep -m NUM / --max-count = NUM но это останавливает чтение файла после совпадения NUM строк , а не шаблона, и, к сожалению, все файлы в этой папке представляют собой одну длинную строку.

Может ли grep остановиться после первого сопоставленного шаблона, или я должен сделать это с чем-то другим?

* Бонусный вопрос - Возможность вывести его как "Совпадающий шаблон", "Имя файла", чтобы я мог его отсортировать?

Ответы [ 3 ]

0 голосов
/ 25 августа 2011

Насколько я знаю, в grep нет способа сделать это.На самом деле, большинство утилита Unix упрямо построчный.Я не думаю, что есть способ напечатать, например, только часть строки в sed.Даже в awk, возможно, есть способ объединить что-то вместе, но я предполагаю, что это было бы неудовлетворительно.

Если в вашей системе есть GNU awk, попробуйте следующее:*

RS устанавливает разделитель записей (обычно новую строку) в качестве шаблона.RT - это текст, соответствующий RS.FILENAME говорит само за себя.exit останавливает исполнение.Таким образом, после первой записи awk напечатает текст шаблона и имя файла и завершит работу.Это не будет работать с более чем одним файлом, потому что выход здесь безусловный.

Если вам нужно запустить это для всех файлов в структуре каталогов, используйте find и xargs, и donне выходить, если вы что-то не нашли:

find . -type f -print0 | xargs -0 gawk 'BEGIN {RS="pattern"} {print RT, FILENAME; if (RT != "") exit}'

Это распечатывает имена файлов (с предшествующим пробелом) всего без шаблона, но печатает шаблон и имя файла, когда оно достигает первого шаблона,затем останавливается.

Конечно, вы должны быть немного осторожны с этой командой: поскольку разделитель записей может вообще отсутствовать, gawk может вылить все содержимое файла в его буфер, иможет не хватить памяти.(Когда я проверил это на моей системе, я получил ошибку в 490 МБ.)

0 голосов
/ 25 августа 2011

На ум приходят две идеи;

perl -nle '/(Rows="[^#][^"]*")/ or continue; print $ARGV, ":", $1; exit 0' files ...

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

for file in *; do
    # Replace every R with newline,
    # and every newline with dot.
    # Your tr's syntax for newline may be different
    tr 'R\n' '\n.'  < "$file" |
    sed -n '/^\(ows="[^#][^"]*"\).*/{;s%%'"$file:"'R\1%;p;q;}'
done

Ваши tr и sed могут отличаться от моих, поэтому это может потребовать некоторой адаптации.

Редактировать: добавлен цикл, замените grep на sed.

0 голосов
/ 06 мая 2011

Я не проверял, но я бы попробовал:

find -type f -print0 | xargs -0 -r cat | grep -m 1 -i -o 'Rows="[^#][^"]*"'
...