Удаление блоков текста из огромного текстового файла - PullRequest
6 голосов
/ 01 февраля 2012

Мне было поручено что-то довольно болезненное, и мне было интересно, может ли кто-нибудь помочь.

Наш поставщик предоставил файл SNMP MIB (TXT). К сожалению, очень большая часть этого файла устарела и должна быть удалена для нашего приложения для мониторинга.

Я пытался сделать это вручную, но это более 800 000 строк, и это подрывает мою волю к жизни.

Структура выглядит примерно так:

-- /*********************************************************************************/
-- /* MIB table for Hardware                                                        */
-- /* Valid from: 543.44                                                            */
-- /* Deprecated from: 600.3                                                        */
-- /*********************************************************************************/

Some text 
some text 
Some text

-- /*********************************************************************************/
-- /* MIB table for Hardware                                                        */
-- /* Valid from: 543.44                                                            */
-- /*********************************************************************************/

Some text 
some text 
Some text

-- /*********************************************************************************/
-- /* MIB table for Hardware                                                        */
-- /* Valid from: 364.44                                                            */
-- /* Deprecated from: 594.3                                                        */
-- /*********************************************************************************/

Повторяется случайным образом и до тошноты

То, что я думаю, это сценарий, который будет:

найти текст "Устаревший от" то

delete that line, 
delete the preceding 3 lines, 
delete the following one line, 
delete then all following lines until the next
"-- /*********************************************************************************/"

Имеет ли это смысл? Возможно ли это, или я только сплю?

Thankyou!

Ответы [ 4 ]

3 голосов
/ 01 февраля 2012

Редактировать: Я только что понял, что прочитал ваш вопрос неправильно, даже после того, как за него проголосовали несколько раз. Мой ответ раньше был выключен! Теперь он должен быть более правильным, но с некоторыми дополнительными допущениями . Простые решения могут дать вам только так далеко!

Это может помочь вам, с некоторыми предположениями:

cat -s data | awk -vFS='\n' -vRS='\n\n' '/Deprecated from/ { getline; next } 1'

Команда cat просто предназначена для того, чтобы выжать лишние символы новой строки, поэтому awk может работать легче. Что касается awk, -vFS='\n' сообщает, что поля разделены символами новой строки, а -vRS='\n\n' сообщает, что записи разделены двумя символами новой строки подряд. Затем /Deprecated from/ находит записи с этим текстом, а { getline; next } читает следующую запись после нее и заставляет ее двигаться дальше. 1 - это ярлык для печати строк, которые достигают следующей точки.

Это предполагает следующее :

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

Так что это может быть не совсем идеально для вас. Если эти предположения верны, то, как вы видите, awk хороший выбор для этой работы: скрипт крошечный!

$ cat -s data | awk -vFS='\n' -vRS='\n\n' '/Deprecated from/ { getline; next } 1'
-- /*********************************************************************************/
-- /* MIB table for Hardware                                                        */
-- /* Valid from: 543.44                                                            */
-- /*********************************************************************************/
Some text
some text
Some text

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

$ cat -s data | awk -vFS='\n' -vRS='\n\n' '/Deprecated from/ { getline; next } { printf "%s\n\n", $0 }'
-- /*********************************************************************************/
-- /* MIB table for Hardware                                                        */
-- /* Valid from: 543.44                                                            */
-- /*********************************************************************************/

Some text
some text
Some text
2 голосов
/ 01 февраля 2012

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

 sed '$!N;$!N;:a;$q;N;/Deprecated from/!{P;s/^[^\n]*\n//;ba};$d;$!N;$d;s/.*//;:b;$d;N;/^\n-- \/\*\+\/$/!{s/.*//;bb};D' file

Вот немного более простое решение (менее эффективное, так как требуется 2 прохода):

awk '/Deprecated from/{a=NR-3;getline;next};a>0 && /^-- \/\*+\/$/{b=NR-1;print a "," b "d";a=b=0};END{if(a>0)print a ",$d"}' file |
sed -f - file
1 голос
/ 01 февраля 2012

Я очень согласен с комментарием об использовании другого языка сценариев для решения этой проблемы. Ruby, Perl или Python, вероятно, будут лучше. Но для забавы вот уродливый скрипт Awk, который делает это. Матчи могут использовать некоторые работы, если они не подходят. Но это реализует простой конечный автомат. Он отслеживает, находится ли он в заголовке или нет, и определяет, является ли он устаревшим. Он хранит строки заголовка в массиве. Когда он достигает конца заголовка, он печатает заголовок (если не устарел). Если заголовок отсутствует, он печатает строки, если предыдущий раздел не устарел.

{
   if ( $0 ~ /-- \/\**+\// ) {
      # This matches one of the -- /*********...****/ lines
      if ( headercount > 0 ) {
         # this must be the closing line in the header
         if ( !deprecated ) {
            for ( i = 0; i < headercount; i++ ) {
                print headers[i]
            }
            # print closing line
            print
         } # if not deprecated

         headercount = 0
      }
      else {
         # must be starting a new section
         headers[0] = $0
         headercount = 1
         deprecated = 0
      }
   }
   else {
      if ( headercount == 0 ) {
         # not in a header section - print if not deprecated
         if ( !deprecated ) {
            print
         }
      }
      else {
         # in a header section - track if it is a deprecated section
         if ( $0 ~ /Deprecated from/ ) {
            deprecated = 1
         }
         # store the header info to dump when we hit the end
         headers[headercount++] = $0;
      }

   }
}
1 голос
/ 01 февраля 2012

Это простой vim макрос.

  1. Откройте файл: $ vim filename
  2. Нажмите q a для записи макроса в регистр a
  3. Введите /Deprecated from:, а затем Enter (для поиска текста)
  4. 3k (подняться на 3 строки)
  5. 4dd (удалить эту строку и следующие 3)
  6. d/\*\*\*\*\*\* (для удаления строк до разделителей)
  7. (при необходимости) нажмите dd (чтобы удалить текущую строку)
  8. Нажмите q, чтобы завершить запись макроса

  9. Введите 1000000@a (чтобы выполнить макрос миллион раз)

...