使用AWK和BASH将大型压缩文件拆分为多个输出

时间:2011-07-20 18:55:15

标签: linux bash awk split gzip

我有一个包含两个字段的大型(3GB)gzip压缩文件:NAME和STRING。我想将此文件拆分为较小的文件 - 如果字段1是john_smith,我希望将字符串放在john_smith.gz中。注意:字符串字段可以并且确实包含特殊字符。

我可以使用BASH轻松地在域中进行for循环,但我更喜欢使用AWK一次读取文件的效率。

我尝试在awk中使用系统函数,并在字符串

周围使用转义单引号

zcat large_file.gz | awk'{system(“echo -e'”'“'”$ 1“\ t”$ 2“'”'“'| gzip>>”$ 1“.gz”);}'

并且它在大多数行上都能正常工作,但是其中一些行被打印到STDERR并且给出了shell无法执行命令的错误(shell认为字符串的一部分是命令)。看起来特殊字符可能会破坏它。

有关如何解决此问题的任何想法,或任何有用的替代实现?

谢谢!

-Sean

4 个答案:

答案 0 :(得分:2)

你面临着与磁盘空间相关的重大折衷。 我假设你试图通过将记录附加到$ {name} .gz文件的末尾来节省空间。 @sehe评论和代码绝对值得考虑。

无论如何,你的时间比3 GB的磁盘空间更有价值。为什么不试试

 zcat large_file.gz \
 | awk '-F\t' { 
    name=$1; string=$2; outFile=name".txt"
    print name "\t" string >> outFile
    # close( outFile) 
   }'

 echo *.txt | xargs gzip -9

您可能需要取消注释#close(outFile)。 包含xargs是因为我假设您将创建更多的1000个文件名。即使你不这样做也不会伤害使用这种技术。

请注意,此代码假设制表符分隔数据,根据需要更改-F的arg值以及打印参数中的“\ t”以提供所需的字段分隔符。

没时间测试这个。如果您喜欢这个想法并且卡住了,请发布您收到的小样本数据,预期输出和错误消息。

我希望这会有所帮助。

答案 1 :(得分:0)

创建此程序,例如largesplitter.c并使用命令

zcat large_file.gz | largesplitter

朴实无缺的计划是:

#include <errno.h>
#include <stdio.h>
#include <string.h>

int main (void)
{
        char    buf [32000];  // todo:  resize this if the second field is larger than 
        char    cmd [120];
        long    linenum = 0;
        while (fgets (buf, sizeof buf, stdin))
        {
                ++linenum;
                char *cp = strchr (buf, '\t');   // identify first field delimited by tab
                if (!cp)
                {
                        fprintf (stderr, "line %d missing delimiter\n", linenum);
                        continue;
                }
                *cp = '\000';  // split line
                FILE *out = fopen (buf, "w");
                if (!out)
                {
                        fprintf (stderr, "error creating '%s': %s\n", buf, strerror(errno));
                        continue;
                }
                fprintf (out, "%s", cp+1);
                fclose (out);
                snprintf (cmd, sizeof cmd, "gzip %s", buf);
                system (cmd);
        }
        return 0;
}

这在我的系统上编译没有错误,但我还没有测试它的功能。

答案 2 :(得分:0)

也许尝试一下以下几点:

zcat large_file.gz | echo $("awk '{system("echo -e '"'"'"$1"\t"$2"'"'"' | gzip >> "$1".gz");}'")

我自己没有尝试过,因为我没有任何大文件可供使用。

答案 3 :(得分:0)

这个小的perl脚本很好地完成了这项工作

  • 保持所有目标文件的性能
  • 做错误基本处理
  • 修改现在也可以通过 gzip 管道输出

$fh存在一些问题,因为显然直接使用哈希条目不起作用

#!/usr/bin/perl
use strict;
use warnings;

my $suffix = ".txt.gz";

my %pipes;
while (my ($id, $line) = split /\t/,(<>),2)
{
    exists $pipes{$id} 
        or open ($pipes{$id}, "|gzip -9 > '$id$suffix'") 
        or die "can't open/create $id$suffix, or cannot spawn gzip";

    my $fh = $pipes{$id};
    print $fh $line;
}

print STDERR "Created: " . join(', ', map { "$_$suffix" } keys %pipes) . "\n"

哦,像

一样使用它
zcat input.gz | ./myscript.pl