Исправление поврежденных файлов mbox с помощью sed/awk

У меня есть куча старых, унаследованных файлов mbox, которые я хочу преобразовать в maildir. Проблема: mbox'ы не полностью совместимы с RFC. В нескольких почтовых ящиках отсутствует пустая строка перед строкой ^From в некоторых (но не во всех) сообщениях, из-за чего mb2md не отделяет эти сообщения друг от друга.

Пример:

...
Text of mail 1
... bla....    
To unsubscribe, visit https:...                      
From fetchmail Fri Nov  8 18:35:54 CET 2002          ## ^missing empty line above
...
Text of mail 2
...

Теперь я ищу простой способ вставить пустую строку перед любой строкой, соответствующей ^From, но только тогда, когда ей не предшествует пустая строка. Своего рода потоковое редактирование необходимо, потому что почтовые ящики могут быть очень большими.

Я регулярно использую sed, но я не знаком с многострочным сопоставлением. Сегодня попробовал несколько вещей (вырезать и вставить с модификациями) без успеха :(

Последняя попытка была sed -E ':a;N;$!ba;s/\n(..*)\nFrom /\n\1\n\nFrom /g' /tmp/testfile

который соответствует только последнему вхождению шаблона!?

sed/awk-experts - есть ли у вас какой-нибудь намек?


person Frank    schedule 10.09.2020    source источник
comment
См. linux.die.net/man/1/formail. работа для вас более надежна, чем любой сценарий, который вы могли бы написать вручную.   -  person Ed Morton    schedule 10.09.2020


Ответы (2)


Каждый раз, когда вы используете конструкции sed, отличные от s, g и p (с -n), вы используете неправильный инструмент. Если по какой-то причине вы не можете использовать formail, просто используйте awk:

$ awk '/^From/ && p{print ""} {p=NF; print}' file
...
Text of mail 1
... bla....
To unsubscribe, visit https:...

From fetchmail Fri Nov  8 18:35:54 CET 2002          ## ^missing empty line above
...
Text of mail 2
...

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

person Ed Morton    schedule 10.09.2020
comment
Я думаю, у вас должен быть пробел здесь /^From /, потому что в письмах обычно есть строки, начинающиеся с From:. - person Luuk; 10.09.2020
comment
Да, этот подход очень хрупок. formail это путь. - person Ed Morton; 11.09.2020

который соответствует только последнему вхождению шаблона!?

Да. Regex жадный. .* соответствует всем, затем, после того, как все соответствует, сопоставляется последний одиночный \nFrom. Сопоставьте все, кроме новой строки, чтобы соответствовать одной строке.

sed -z -E 's/(\n[^\n]+\n)(From )/\1\n\2/g'

Если вы не хотите читать весь файл в память, вы должны прочитать как минимум две строки в памяти. Ниже я помещаю предыдущую строку в область хранения - добавляю текущую строку с предыдущей строкой в ​​каждую прочитанную строку, чтобы проверить условие. После проверки печатается предыдущая строка.

sed -n -E '
      # Hold first line.
      1{h;b}
      # Append the line to hold space and switch hold space with pattern space
      # so that we have previous\ncurrent lines in pattern space.
      H;x
      # If we have From prepended by anything in previous line, add a newline
      /.+\nFrom /s/\n/\n\n/
      # Remove current line
      s/\n[^\n]*$//
      # Print previous line. Maybe with extra newline.
      p
      # If its last line, also print the holded last line
      ${x;p}
'

и однострочник:

sed -nE '1{h;b};H;x;/.+\nFrom /s/\n/\n\n/;s/\n[^\n]*$//p;${x;p}'
person KamilCuk    schedule 10.09.2020
comment
Для этого потребуется прочитать весь файл в память, и OP сказал, что файлы could be really huge. Лично я бы даже не рассматривал sed для такой работы. - person Ed Morton; 10.09.2020
comment
не знаю, что делает :a;N;$!ba. Призвать Ктулху? Менее шутливо - я предполагаю, что он что-то делает с конкатенацией входных строк в каком-то удержании/буфере, но я понятия не имею, занимает ли он 2 строки за раз или читает весь файл сразу или что-то еще, и я не знаю, если это только POSIX, или только GNU, или что-то еще. Просто набор мистических рун, насколько я знаю, но тогда я использую sed только около 40 лет :-). - person Ed Morton; 10.09.2020