Удаление блока между двумя маркерами регулярных выражений при сопоставлении шаблона внутри блока - PullRequest
1 голос
/ 29 января 2020

Давайте предположим следующую структуру:

  -   key1: value11
      key2:
      - value21
      - value22
      - value23
      key3: value31
      key4:
      - value41
      - value42
      key5: value51
  -   key1: value12
      key2:
      - value24
      - value25
      key3: value32
      key5: value52
  -   key1: value13
      key2:
      - value26
      key3: value33
      key4:
      - value43
      - value44
      - value45
      key5: value53

Можно ли удалить все блоки между (и включая) регулярным выражением маркера начала и конца:

 - begin marker: '^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$'
 - end marker:   '^[[:blank:]]{6}key5:[[:blank:]].+$'

, когда следующее Регулярное выражение сопоставляется внутри блока (ов):

matching pattern: '^[[:blank:]]{6}key3:[[:blank:]]value32$'?

Цель состоит в том, чтобы получить:

  -   key1: value11
      key2:
      - value21
      - value22
      - value23
      key3: value31
      key4:
      - value41
      - value42
      key5: value51
  -   key1: value13
      key2:
      - value26
      key3: value33
      key4:
      - value43
      - value44
      - value45
      key5: value53

Маркер начала также может служить маркером конца, если вхождение второго маркера не удаляется при удалении блока.

Я безуспешно пробовал несколько подходов с помощью sed / awk, например, этот, вдохновленный пунктом 4.21 этого сообщения :

sed ':t
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/ {      # For each line between these block markers
        /^[[:blank:]]{6}key5:[[:blank:]].+$/!{                                                  # If we are not at the /end/ marker
                $!{                                                                             # nor the last line of the file
                        N;                                                                      # add the Next line to the pattern space
                        bt
                }                                                                               # and branch (loop back) to the :t label
        }                                                                                       # This line matches the /end/ marker
        /^[[:blank:]]{6}key3:[[:blank:]]value32$/d;                                             # If /regex/ matches, delete the block
}' file

Ответы [ 4 ]

2 голосов
/ 30 января 2020

Формат файла выглядит как YAML. Тогда почему бы вам не использовать yq для фильтрации? Тогда вы можете просто сказать:

yq -y '[ .[] | select (.key3 != "value32") ]' file

, что приводит к:

- key1: value11
  key2:
  - value21
  - value22
  - value23
  key3: value31
  key4:
  - value41
  - value42
  key5: value51
- key1: value13
  key2:
  - value26
  key3: value33
  key4:
  - value43
  - value44
  - value45
  key5: value53

Возможно, вам придется установить yq с pip install yq или чем-то подобным.

1 голос
/ 30 января 2020

Это может работать для вас (GNU sed):

sed -E '/^\s{2}-\s{3}key1:\s/{:a;N;/^\s{6}key5:\s/M!ba;/^\s{6}key3:\svalue32$/Md}' file

Соберите группу строк между key1 и key5 и, если группа содержит нужную строку, удалите всю группу.

NB. Использование флага M, позволяющего многострочные совпадения.

По сути:

sed '/key1/{:a;N;/key5/!ba;/key3.*value32$/Md}' file
1 голос
/ 29 января 2020

sed - это правильный инструмент для выполнения s/old/new/ для отдельных строк, вот и все. Для чего-то более интересного вы должны использовать awk для ясности, переносимости, надежности, эффективности и т. Д. c.

На самом деле вам не нужно первое регулярное выражение, которое вы указываете с учетом введенного вами примера ввода / вывода, например с GNU awk для мульти-символьных RS и RT:

awk -v RS='[[:blank:]]{6}key5:[[:blank:]][^\n]+\n' -v ORS= '
    !/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/{ print $0 RT }
' file
  -   key1: value11
      key2:
      - value21
      - value22
      - value23
      key3: value31
      key4:
      - value41
      - value42
      key5: value51
  -   key1: value13
      key2:
      - value26
      key3: value33
      key4:
      - value43
      - value44
      - value45
      key5: value53

или с любым awk:

awk '
{ rec = rec $0 ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
    if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
        printf "%s", rec
    }
    rec=""
}
' file
  -   key1: value11
      key2:
      - value21
      - value22
      - value23
      key3: value31
      key4:
      - value41
      - value42
      key5: value51
  -   key1: value13
      key2:
      - value26
      key3: value33
      key4:
      - value43
      - value44
      - value45
      key5: value53

, но вы также можете использовать это первое регулярное выражение, например:

awk '
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/ { inBlock=1 }
inBlock { rec = rec $0 ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
    if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
        printf "%s", rec
    }
    rec=""
    inBlock=0
}
' file
  -   key1: value11
      key2:
      - value21
      - value22
      - value23
      key3: value31
      key4:
      - value41
      - value42
      key5: value51
  -   key1: value13
      key2:
      - value26
      key3: value33
      key4:
      - value43
      - value44
      - value45
      key5: value53
0 голосов
/ 30 января 2020

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

/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/{
   /^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/h
   //!H
   /^[[:blank:]]{6}key5:[[:blank:]].+$/{
     g
     /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/!p
   }
   d
}

Вышеприведенное должно выполняться с sed -Ef cmdfile file.

Одна из нескольких неприятностей, связанных с этим, заключается в необходимости повторять шаблоны.

...