2011-12-17 59 views
33

我有一個大約400mb的電子郵件轉儲。我想分解成.txt文件,每個文件中包含一個郵件。每封電子郵件都以標準HTML標題指定文檔類型開始。根據內容在Linux中拆分文件

這意味着我將不得不根據上述表頭分割我的文件。我如何在Linux中去解決它?

+0

這真的是一個電子郵件轉儲嗎?你的意思是你根本沒有郵件頭?什麼是你稱爲「標準的HTML標題指定DOCTYPE」? – fge 2011-12-17 10:52:23

+0

「<!DOCTYPE html PUBLIC \」 - // W3C // DTD HTML 4.01 Transitional // EN \「> 接下來是整個電子郵件! – Greenhorn 2011-12-17 10:53:55

回答

54

如果你有一個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 
1

這對一些perl「魔法」是可行的......許多人會稱這個醜陋的,但在這裏。

關鍵是你想要的東西來代替$/和閱讀你的輸入,因爲這樣的:

#!/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; 
} 

編輯:固定的,我總是忘了$/包括在輸入。此外,第一個文件將始終爲空,但可以輕鬆處理。

1

我同意fge。與perl它會更簡單。你可以嘗試這樣的事情 -

#!/usr/bin/perl 

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

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

用你的標題類型替換HEADER_FORMAT

+0

是的,積極的向前看會很好地工作,特別是因爲這裏頭沒有包含任何元字符,甚至可以使用`qr //`建立拆分正則表達式。 – fge 2011-12-17 11:16:37

4

csplit程序優雅解決您的問題:

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

參數的順序錯誤和缺失重複實際上不能如願。 – qwertzguy 2017-06-24 01:00:45

2

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