Удаление блоков кода, удовлетворяющих условию

#linux #awk #sed #grep

#linux #awk #sed #grep

Вопрос:

У меня очень большой текстовый файл, который в основном представляет собой журнал сообщений с { разделителем между сообщениями. Я хочу удалить блоки между { , если они соответствуют определенному условию. В приведенном ниже примере я хочу удалить средний блок сообщений, который имеет EVENT_TYPE = BDE , и оставить два ABC сообщения. Файл находится в ящике Linux, поэтому у меня есть доступ ко всем вашим обычным grep , sed , awk и т. Д. Я могу использовать эти процессы, чтобы найти EVENT_TYPE , но не уверен, как затем найти больший блок и удалить его.

 }
/type/ - DataEvents = {
VALUE = 2342
EVENT_TYPE = ABC
VALUE_YESTERDAY = 1299
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
} 

/type/ - DataEvents = {
VALUE = 889
EVENT_TYPE = BDE
VALUE_YESTERDAY = 778 
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
} 

/type/ - DataEvents = {
VALUE = 123
EVENT_TYPE = ABC
VALUE_YESTERDAY = 345
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
} 
  

Комментарии:

1. Должен ли быть инициал } в верхней части образца? Если все эти блоки разделены хотя бы одной пустой строкой (до и после), это становится намного проще.

2. этого может быть достаточно: awk 'BEGIN{RS=ORS="}"} !/EVENT_TYPE = BDE/ amp;amp; NF' file

Ответ №1:

Всякий раз, когда входные данные имеют пары имя = значение, я считаю наиболее удобным / надежным / поддерживаемым / расширяемым сначала создать массив ( f[] ниже) этого сопоставления для каждой записи ( rec ниже), а затем получить доступ к значениям по их именам для тестирования, печати и т.д.:

 $ cat tst.awk
BEGIN { FS=" *= *" }
NF { rec = rec $0 ORS; f[$1] = $2 }
/^}/ {
    if ( f["EVENT_TYPE"] != "BDE" ) {
        print rec
    }
    rec = ""
    delete f
}

$ awk -f tst.awk file
/type/ - DataEvents = {
VALUE = 2342
EVENT_TYPE = ABC
VALUE_YESTERDAY = 1299
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}

/type/ - DataEvents = {
VALUE = 123
EVENT_TYPE = ABC
VALUE_YESTERDAY = 345
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}
  

Ответ №2:

Используя gawk для многосимвольных RS, вы можете настроить RS так, чтобы каждый блок обрабатывался как отдельная запись, а затем просто тестировался внутри записи, как обычно. Мы используем NR > 1 , чтобы игнорировать нежелательную первую (пустую) запись, которая возникает из данных, начинающихся с разделителя записей (предполагая, что это так, это неясно из вашего частичного примера).

 $ gawk -v RS='/type/' 'NR > 1 amp;amp; !/EVENT_TYPE = BDE/ { printf "/type/%s", $0 }' file
/type/ - DataEvents = {
VALUE = 2342
EVENT_TYPE = ABC
VALUE_YESTERDAY = 1299
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}

/type/ - DataEvents = {
VALUE = 123
EVENT_TYPE = ABC
VALUE_YESTERDAY = 345
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}
  

Ответ №3:

Не могли бы вы попробовать следующее.

 awk '
/{/{
  val=""
}
/}/{
  if(found=="" amp;amp; val){
     print val ORS $0
  }
  found=val=""
  next
}
/EVENT_TYPE = BDE/{
  found=1
}
{
  val=(val?val ORS:"")$0
}
END{
  if(val amp;amp; found==""){
    print val ORS $0
  }
}
'   Input_file
  

Ответ №4:

Как насчет

 $ vim -es ' g/EVENT_TYPE = BDE/exe "norm! dap"' ' %print' ' q!' file
  

Вывод:

 }
/type/ - DataEvents = {
VALUE = 2342
EVENT_TYPE = ABC
VALUE_YESTERDAY = 1299
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}

/type/ - DataEvents = {
VALUE = 123
EVENT_TYPE = ABC
VALUE_YESTERDAY = 345
HAS_DELAY = false
SEND_TIME_RT = 18:55:21.224 00:00
}
  

Ответ №5:

если ваши данные в ‘d’ по gnu sed

 sed -Ez 's/{[^{}]*EVENT_TYPEs*=s*BDE[^}]*}//' d
  

Ответ №6:

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

 sed '/{/{:a;N;/}/!ba;/EVENT_TYPE = BDE/d}' file
  

Соберите строки между { и } и, если эти строки содержат EVENT_TYPE = BDE , удалите их.