Подтвердить что ты не робот

Разделение файла в linux на основе содержимого

У меня есть дамп электронной почты размером около 400 МБ. Я хочу разбить это на .txt файлы, состоящие из одной почты в каждом файле. Каждое электронное письмо начинается со стандартного HTML-заголовка, определяющего doctype.

Это означает, что мне придется разделить мои файлы на основе вышеупомянутого заголовка. Как мне это сделать в Linux?

4b9b3361

Ответ 1

Если у вас есть mail.txt

$ cat mail.txt
<html>
    mail A
</html>

<html>
    mail B
</html>

<html>
    mail C
</html>

запустить csplit для разделения на <html>

$ csplit mail.txt '/^<html>$/' '{*}'

 - mail.txt    => input file
 - /^<html>$/  => pattern match every `<html>` line
 - {*}         => repeat the previous pattern as many times as possible

проверить выход

$ ls
mail.txt  xx00  xx01  xx02  xx03

Если вы хотите сделать это в awk

$ awk '/<html>/{filename=NR".txt"}; {print >filename}' mail.txt
$ ls
1.txt  5.txt  9.txt  mail.txt

Ответ 2

Программа csplit позволяет решить вашу проблему элегантно:

csplit '/<!DOCTYPE.*/' $FILE

Ответ 3

csplit - лучшее решение этой проблемы. Просто подумал, что я опубликую bash -решение, чтобы показать, что нет необходимости идти perl в этой задаче:

#!/usr/bin/bash

MAIL='mail'        # path to huge mail-file

#get linenumbers for all headers
line_no=$(grep -n html $MAIL | cut -d: -f1)

read -a LINES<<< $line_no

file=0
for i in $(seq 0 2 ${#LINES[@]}); do
    start=${LINES[i]}
    end=$((${LINES[i+1]}-1))
    echo $start, $end
    sed -n "${start},${end}p" $MAIL > ${MAIL}${file}.txt
    file=$((file+1))
done

Ответ 4

Это выполнимо с некоторыми perl "magic"... Многие люди назвали бы это уродливым, но здесь идет.

Трюк состоит в том, чтобы заменить $/ на то, что вы хотите, и прочитать свой ввод, как таковой:

#!/usr/bin/perl -W
use strict;
my $i = 1;

$/ = <<EOF;
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head> <xmeta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
EOF

open INPUT, "/path/to/inputfile" or die;

while (my $mail = <INPUT>) {
    $mail = substr($mail, 0, index($mail, $/));
    open OUTPUT, ">/path/to/emailfile." . $i . ".txt" or die;
    $i++;
    print OUTPUT $mail;
    close OUTPUT;
}

edit: fixed, я всегда забываю, что $/ включен во вход. Кроме того, первый файл всегда будет пустым, но тогда его можно легко обработать.

Ответ 5

Я согласен с fge. С perl было бы намного проще. Вы можете попробовать что-то вроде этого -

#!/usr/bin/perl

undef $/;
$_ = <>;
$n = 0;

for $match (split(/(?=HEADER_FORMAT)/)) {
      open(O, '>mail' . ++$n);
      print O $match;
      close(O);
}

Замените HEADER_FORMAT на свой тип заголовка.