Совпадение регулярных выражений в нескольких строках в bash - PullRequest
1 голос
/ 19 июня 2019

Я хочу сопоставить все шаблоны, которые начинаются с [% и заканчиваются %] в файле.

Я пробовал несколько инструментов, таких как awk, sed, pcregrep, и ни один из них, похоже, неработать, хотя они предлагаются в качестве лучших ответов на подобные вопросы.

[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]: {
        const [% selectionType %]& source = this->[% selectionName %]();
        rc = bcem_AggregateUtil::toAggregate(result,
                                             d_selectionId,
                                             source);
      } break;
[% END -%]

[% foo ]

[% INCLUDE attributeSearchBlock

    tree=attributeSearchTree depth=0

    visit='ReturnAttributeInfo' name='name' nameLength='nameLength' -%]

Для кода выше, я ожидаю следующий результат:

[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]: {
        const [% selectionType %]& source = this->[% selectionName %]();
[% END -%]
[% INCLUDE attributeSearchBlock

    tree=attributeSearchTree depth=0

    visit='ReturnAttributeInfo' name='name' nameLength='nameLength' -%]

Но я получаю все строки совпадаютвместо этого.

Что я делаю не так?

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:

Если оно в нескольких строках, оно также должно совпадать.Например:

[% foo
bar -%]

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ 2: Кажется, что ни один из ответов не работает, поэтому я сделал все вручную, используя следующее:

        hasPatternStarted=false
        while read -r line; do
            if [[ $line =~ '[%' ]]; then
                hasPatternStarted=true
            fi
            if [[ $line =~ '%]' ]]; then
                hasPatternStarted=false
                echo $line
            fi
            if [ "$hasPatternStarted" = true ]; then
                echo $line
            fi
        done < "$filename"

Он работает нормально, но если у кого-то есть один вкладыш для решения этой проблемы (используя sed, awek, pcregrep, perl, grep что угодно), скажите, пожалуйста, так.

Ответы [ 3 ]

1 голос
/ 19 июня 2019

Если вы посмотрите на то, что вы просите, вы получите две строки, так как только две заканчиваются на -%]

 awk '/\[%.*-%\]/' file
[% FOREACH selection = selections -%]
[% END -%]

Вы можете сделать это, чтобы получить результат со всеми, начинающимися с [% и заканчивающимисяс %]

awk '/\[%.*%\]/' file
[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]: {
        const [% selectionType %]& source = this->[% selectionName %]();
[% END -%]
0 голосов
/ 19 июня 2019

TL; DR: perl -ne 'print if /\[%/../%\]/' file

Вы могли бы подумать, что могли бы сделать это: sed -n '/[%/,/%]/p', но он не завершается должным образом.

ИтакВы можете преобразовать вышеупомянутое в perl: perl -ne 'print if /\[%/.../%\]/', и это имеет ту же проблему из-за оператора ....

В Perl есть оператор для сохранения дня здесь: perl -ne 'print if /\[%/../%\]/'

Как perlop говорит:

В скалярном контексте ".." возвращает логическое значение.Оператор является бистабильным, как триггер, и эмулирует оператор диапазона строк (запятая) sed, awk и различных редакторов.Каждый оператор «..» поддерживает свое собственное логическое состояние, даже при вызовах подпрограммы, которая его содержит.Это ложно, пока его левый операнд ложен.Если левый операнд равен true, оператор диапазона остается истинным, пока правый операнд не станет true, ПОСЛЕ того, как оператор диапазона снова становится ложным.Это не становится ложным, пока в следующий раз оператор диапазона не будет оценен.Он может проверить правильный операнд и стать ложным при той же оценке, в которой он стал истинным (как в awk), но он все равно возвращает истину один раз. Если вы не хотите, чтобы он проверял правильный операнд до следующей оценки, как в sed, просто используйте три точки ("...") вместо двух.Во всем остальном, «...» ведет себя так же, как и «..».

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

0 голосов
/ 19 июня 2019

Это один из способов использования GNU awk для RS и RT с несколькими символами:

$ awk -v RS='%]' -v ORS= '{print gensub(/.*(\n[^\n]*\[%)/,"\\1",1) RT}' file
[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]
        const [% selectionType %]& source = this->[% selectionName %]
[% END -%]
[% INCLUDE attributeSearchBlock

    tree=attributeSearchTree depth=0

    visit='ReturnAttributeInfo' name='name' nameLength='nameLength' -%]

, а вот другой способ с RS и FPAT с несколькими символами:

$ cat tst.awk
BEGIN {
    RS = "^$"
    FPAT = "[^\n]*{[^{}]*}"
}
{
    gsub(/@/,"@A"); gsub(/{/,"@B"); gsub(/}/,"@C")
    gsub(/\[%/,"{")
    gsub(/%\]/,"}")
    for (i=1; i<=NF; i++) {
        str = $i
        gsub(/}/,"%]",str)
        gsub(/{/,"[%",str)
        gsub(/@C/,"}",str); gsub(/@B/,"{",str) gsub(/@A/,"@",str)
        print str
    }
}

$ awk -f tst.awk file
[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]
        const [% selectionType %]& source = this->[% selectionName %]
[% END -%]
[% INCLUDE attributeSearchBlock

    tree=attributeSearchTree depth=0

    visit='ReturnAttributeInfo' name='name' nameLength='nameLength' -%]

Второй сценарийдемонстрирует общую идиому при использовании такого инструмента, как awk или sed, который поддерживает только жадные совпадения, но вам нужно сопоставлять текст между многосимвольными строками, то есть преобразовывать эти многосимвольные строки-разделители в отдельные символы, чтобы затем можно было использовать класс отрицанных символовмежду ними.

Таким образом, в приведенном выше примере:

gsub(/@/,"@A"); gsub(/{/,"@B"); gsub(/}/,"@C")

Я конвертирую все @ с в @A с, чтобы освободить символ @, а затем конвертирую все { s до @B s (теперь это строка, которую, как мы ЗНАЕМ, на входе нет, поскольку мы ставим A после каждого @), а затем преобразуем все } s в @C s, обеспечивая тем самымна входе нет символов { или }, поэтому мы освобождаем их для использования в качестве разделителей регулярных выражений.Теперь я могу сделать:

gsub(/\[%/,"{")
gsub(/%\]/,"}")

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

FPAT = "{[^{}]*}"

В GNU awk назначение FPAT таким образом автоматически сохраняет совпадающие строки в $ 1, $ 2 и т. д., поэтому мне просто нужно разматывать вышеуказанные замены перед печатью каждого поля, следовательно:

gsub(/}/,"%]",str)
gsub(/{/,"[%",str)
gsub(/@C/,"}",str); gsub(/@B/,"{",str) gsub(/@A/,"@",str)

Эквивалент 2-го сценария выше для любого POSIX awk:

$ cat tst.awk
{ rec = (NR>1 ? rec ORS : "") $0 }
END {
    $0 = rec
    FPAT = "[^\n]*[{][^{}]*[}]"
    gsub(/@/,"@A"); gsub(/[{]/,"@B"); gsub(/[}]/,"@C")
    gsub(/\[%/,"{")
    gsub(/%\]/,"}")
    while ( match($0,FPAT) ) {
        str = substr($0,RSTART,RLENGTH)
        $0 = substr($0,RSTART+RLENGTH)
        gsub(/[}]/,"%]",str)
        gsub(/[{]/,"[%",str)
        gsub(/@C/,"}",str); gsub(/@B/,"{",str) gsub(/@A/,"@",str)
        print str
    }
}

$ awk -f tst.awk file
[% FOREACH selection = selections -%]
      case SELECTION_ID_[% SELECTION_NAME %]
        const [% selectionType %]& source = this->[% selectionName %]
[% END -%]
[% INCLUDE attributeSearchBlock

    tree=attributeSearchTree depth=0

    visit='ReturnAttributeInfo' name='name' nameLength='nameLength' -%]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...