pcregrep или grep: поиск с головами не работает - PullRequest
1 голос
/ 24 февраля 2020

Я пытаюсь найти регулярное выражение, которое не работает в pcregrep или grep

Я хочу найти биты секций

  • , которые могут занимать несколько строк,
  • , которые начинаются с PQXY в начале строки и
  • заканчиваются OFEJ в конце строки, а
  • не содержит ни PQXY, ни OFEJ между

В общем, я использую следующее в возвышенном тексте, найдите и хорошо работает

(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)

Теперь я хочу найти количество таких случаев, поэтому я пытаюсь использовать grep или pcergrep, оба не работает.

pcregrep -c "(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)" file.txt
zsh: event not found: PQXY|OFEJ).)

и с grep

$ grep -c -zoP "(?s)(^PQXY(?:(?!PQXY|OFEJTRANS).)*OFEJTRANS\n)" CB_raw_testing_21_feb_CORRECTIONS_0002.txt
zsh: event not found: PQXY|OFEJTRANS).)

Как я могу это сделать

Ответ основан на @paxdiablo и @ anubha.

Основная ошибка заключалась в одинарных кавычках, адресованных @ paxdiablo

$ pcregrep -c -M '(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt 
0

Решением регулярного выражения является добавление (? S) на основе @anubha. Конечно \n также работает вместо (\R|\z)

$ pcregrep -c -M '(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt
11726

Ответы [ 2 ]

2 голосов
/ 24 февраля 2020

zsh: event not found: PQXY|OFEJ).)

Поскольку это zsh повышение ошибки, это почти , конечно, , потому что он пытается обработать вещи в двойных кавычках. Чтобы защитить его от этого, вы должны использовать одинарные кавычки, такие как:

pcregrep -c '(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt

У меня не установлено pcregrep, но вот расшифровка, показывающая проблему только с echo:

pax> echo "(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ)"
zsh: event not found: PQXY|OFEJ).)

pax> echo '(?s)(^PQXY(?:(?OFEJ)'
(?s)(^PQXY(?:(?OFEJ)

С точки зрения решения проблемы вместо использования специального инструмента c , Я бы на самом деле выбрал awk (а) в этом случае. Вы можете сделать что-то вроде:

awk '/^PQXY/     { s = $0; c = 1; next}
     /OFEJ$/     { if (c == 1) { print s""ORS""$0; c = 0 }; next }
     /OFEJ|PQXY/ { c = 0; next }
     c == 1      { s = s""ORS""$0 }' inputFile

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

Затем для каждой строки :

  • Если он начинается с PQXY, сохраните строку и установите флаг сбора, затем go на следующую строку ввода.
  • В противном случае, если он заканчивается на OFEJ и вы собираете, выводите собранный участок и прекращаете сбор, затем go к следующей строке ввода.
  • В противном случае, если в нем есть какая-либо из строк, прекратите сбор, перейдите к следующей строке ввода.
  • В противном случае, если собирать, добавить текущую строку и перейти (неявно) к следующей строке ввода.

Я проверил это с некоторыми ограниченными тестовыми данными, и, похоже, все работает нормально. Вот сценарий bash (b) , который я использовал для тестирования. Вы можете добавить столько тестов, сколько вам нужно, чтобы это решило вашу проблему.

for i in \
    "PQXY 1\nabc\n2 OFEJ\n" \
    "PQXY 1\nabc\n2 OFEJx\n" \
    "PQXY 1\nabc\n  PQXY \n2 OFEJ\n" \
    "PQXY 1\nabc\n  OFEJ \n2 OFEJ\n" \
    "PQXY 1\nabc\ndef\nPQXY 2\n2 OFEJ\n" \
; do
    echo "$i:"
    printf "$i" | awk '
        /^PQXY/     { s = $0; c = 1; next}
        /OFEJ$/     { if (c == 1) { print s""ORS""$0; c = 0 }; next }
        /OFEJ|PQXY/ { c = 0; next }
        c == 1      { s = s""ORS""$0 }' | sed 's/^/    /
    '
done

Вот вывод так что вы можете увидеть это в действии:

PQXY 1\nabc\n2 OFEJ\n:
    PQXY 1
    abc
    2 OFEJ
PQXY 1\nabc\n2 OFEJx\n:
PQXY 1\nabc\n  PQXY \n2 OFEJ\n:
PQXY 1\nabc\n  OFEJ \n2 OFEJ\n:
PQXY 1\nabc\ndef\nPQXY 2\n2 OFEJ\n:
    PQXY 2
    2 OFEJ

(a) По моему опыту, если вы попробовали три вещи с регулярным выражением в стиле grep без успеха обычно быстрее перейти к более сложному инструменту: -)


(b) Да, я знаю, что оно написано в bash, а не zsh, но это потому, что :

  • это программа test , чтобы показать вам, что awk работает, следовательно, используемый язык не имеет значения; и
  • Мне гораздо удобнее с bash tahn zsh: -)
1 голос
/ 24 февраля 2020

Использование gnu grep:

grep -ozP '(?ms)^PQXY(?:(?!PQXY|OFEJ).)*OFEJ(\R|\z)' file
  • Вы должны использовать опцию -z для обработки входных и выходных данных как последовательностей строк, каждая из которых заканчивается нулевым байтом.

  • Убедитесь, что для шаблона используются одинарные кавычки, чтобы модуль истории оболочки не пытался обработать !.

  • Добавлен модификатор (?m) (MULTILINE), позволяющий использовать ^ и $ в регулярном выражении для каждой строки
  • Используется (\R|\z), чтобы завершить шаблон без перевод строки в конец файла. \R соответствует любому индексу перевода строки, включая символы Юникода, а \z соответствует концу ввода.

Рабочая демонстрация


Эквивалентное решение в pcregrep

pcregrep -M '(?s)^PQXY(?:(?!PQXY|OFEJ).)*OFEJ(\R|\z)' file

-M включает многострочную опцию в pcregrep.

...