it-swarm-pt.tech

Dividindo um arquivo no Linux com base no conteúdo

Eu tenho um dump de e-mail de cerca de 400mb. Quero dividir isso em arquivos .txt, consistindo em um email em cada arquivo. Todo email começa com o cabeçalho HTML padrão, especificando o tipo de documento.

Isso significa que terei que dividir meus arquivos com base no cabeçalho acima mencionado. Como eu faço isso no linux?

41
Greenhorn

Se você tem um mail.txt

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

<html>
    mail B
</html>

<html>
    mail C
</html>

execute csplit para dividir por <html>

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

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

verificar saída

$ ls
mail.txt  xx00  xx01  xx02  xx03

Se você quiser fazê-lo em awk

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

O programa csplit resolve seu problema com elegância:

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

csplit é a melhor solução para esse problema. Apenas pensei em publicar uma solução bash para mostrar que não há necessidade de usar o Perl nesta tarefa:

#!/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
2
Fredrik Pihl

É possível com alguma "mágica" do Perl ... Muitas pessoas chamariam isso de feio, mas aqui vai.

O truque é substituir $/ com o que deseja e leia sua entrada, como tal:

#!/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: fixo, eu sempre esqueço que $/ está incluído na entrada. Além disso, o primeiro arquivo sempre estará vazio, mas poderá ser facilmente manipulado.

1
fge

Eu concordo com o fge. Com Perl, seria muito mais simples. Você pode tentar algo assim -

#!/usr/bin/Perl

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

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

Substituir HEADER_FORMAT com o seu tipo de cabeçalho.

1
jaypal singh