【问题标题】:Splitting a file in linux based on content [duplicate]根据内容在linux中拆分文件[重复]
【发布时间】:2012-01-22 13:43:33
【问题描述】:

我有一个大约 400mb 的电子邮件转储。我想将其拆分为 .txt 文件,每个文件中包含一封邮件。每封电子邮件都以指定文档类型的标准 HTML 标头开头。

这意味着我将不得不根据上述标题拆分我的文件。在 linux 中如何处理?

【问题讨论】:

  • 这真的是电子邮件转储吗?你的意思是你根本没有邮件标题?你怎么称呼“指定文档类型的标准 HTML 标头”?
  • ""-//W3C//DTD HTML 4.01 Transitional//EN\"> 这后面是整个电子邮件!

标签: linux file bash sed awk


【解决方案1】:

如果你有mail.txt

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

<html>
    mail B
</html>

<html>
    mail C
</html>

运行csplit&lt;html&gt;分割

$ 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

【讨论】:

  • 我害怕!我做了同样的事情并做了一个 $ls mail.txt xx00 并且显然 mail.txt 与 xx00 相同。任何修复?
  • @Ramprakash 我的csplit 的版本是8.5。也许你的没有重复模式的{*}。请检查手册页。我只是添加awk 解决方案。你可以试试看。
  • @Greenhorn 我的csplit 版本也不支持{*},但这有效:csplit -n 6 -f 'mail-' -k mail.txt '/^&lt;html&gt;$/' '{5000}'
  • 为了防止在第一行与模式不匹配时出现awk 错误(至少对于gawk),请执行以下操作:awk 'BEGIN {filename="0.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】:

    我同意 fge。使用perl 会简单得多。你可以试试这样的 -

    #!/usr/bin/perl
    
    undef $/;
    $_ = <>;
    $n = 0;
    
    for $match (split(/(?=HEADER_FORMAT)/)) {
          open(O, '>mail' . ++$n);
          print O $match;
          close(O);
    }
    

    HEADER_FORMAT 替换为您的标头类型。

    【讨论】:

    • 是的,积极的前瞻会很好地工作,特别是因为这里的标题不包含任何元字符。您甚至可以使用qr// 来构建拆分正则表达式。
    【解决方案5】:

    使用一些 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;
    }
    

    编辑:已修复,我总是忘记 $/ 包含在输入中。此外,第一个文件将始终为空,但随后可以轻松处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-27
      • 2018-03-03
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 1970-01-01
      相关资源
      最近更新 更多