Разделите большой сжатый файл на несколько выходов, используя AWK и BASH - PullRequest
4 голосов
/ 20 июля 2011

У меня есть большой (3 ГБ) сжатый файл, содержащий два поля: NAME и STRING. Я хочу разделить этот файл на более мелкие файлы - если поле 1 - john_smith, я хочу, чтобы строка была помещена в john_smith.gz. ПРИМЕЧАНИЕ: строковое поле может содержать и содержит специальные символы.

Я могу легко сделать это в цикле for для доменов, используя BASH, но я бы предпочел эффективность чтения файла за один раз, используя AWK.

Я попытался использовать системную функцию в awk с экранированными одинарными кавычками вокруг строки

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

и он отлично работает на большинстве строк, однако некоторые из них выводятся на STDERR и выдают ошибку, что оболочка не может выполнить команду (оболочка считает, что часть строки является командой). Похоже, что специальные символы могут сломать его.

Есть мысли о том, как это исправить, или о каких-либо альтернативных реализациях, которые могли бы помочь?

Спасибо!

-Sean

Ответы [ 4 ]

2 голосов
/ 21 июля 2011

Вы столкнулись с большим компромиссом во времени и дисковым пространством.Я предполагаю, что вы пытаетесь сэкономить место, добавляя записи в конец ваших файлов $ {name} .gz.@sehe комментарии и код, безусловно, стоит рассмотреть.

В любом случае ваше время более ценно, чем 3 ГБ дискового пространства.Почему бы не попробовать

 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 имен файлов.Даже если вы этого не сделаете, использование этой техники не помешает.

Обратите внимание, что в этом коде используются данные, разделенные символом табуляции, при необходимости измените значение аргумента arg для -F и "\ t" в задании печатичтобы получить необходимый вам разделитель полей.

У меня нет времени, чтобы проверить это.Если вам нравится эта идея и вы застряли, пожалуйста, опубликуйте небольшие образцы данных, ожидаемый вывод и сообщения об ошибках, которые вы получаете.

Надеюсь, это поможет.

0 голосов
/ 20 июля 2011

Возможно, попробуйте что-то вроде:

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

Я сам не пробовал, так как у меня нет больших файлов для воспроизведения.

0 голосов
/ 21 июля 2011

Этот маленький 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
0 голосов
/ 20 июля 2011

Создайте эту программу как, скажем, 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;
}

Она без ошибок компилируется в моей системе, но у меня естьне проверял его функциональность.

...