unix - chia một tệp .gz lớn theo dòng


16

Tôi chắc chắn ai đó đã có nhu cầu dưới đây, cách nhanh chóng để chia một tệp .gz khổng lồ theo dòng là gì? Tệp văn bản cơ bản có 120 triệu hàng. Tôi không có đủ dung lượng đĩa để nén toàn bộ tệp cùng một lúc nên tôi tự hỏi liệu có ai đó biết về tập lệnh bash / perl hoặc công cụ có thể chia tệp (cả .gz hoặc .txt) thành các tệp dòng 3x 40mn không . tức là gọi nó như:

    bash splitter.sh hugefile.txt.gz 4000000 1
 would get lines 1 to 40 mn    
    bash splitter.sh hugefile.txt.gz 4000000 2
would get lines 40mn to 80 mn
    bash splitter.sh hugefile.txt.gz 4000000 3
would get lines 80mn to 120 mn

Có lẽ đang thực hiện một loạt các giải pháp này hoặc gunzip -c sẽ cần đủ dung lượng để toàn bộ tệp được giải nén (tức là vấn đề ban đầu): gunzip -c hugefile.txt.gz | đầu 4000000

Lưu ý: Tôi không thể lấy thêm đĩa.

Cảm ơn!


1
Bạn có muốn các tập tin kết quả được nén lại?

Bạn có thể sử dụng gunzip trong một ipe. Phần còn lại có thể được thực hiện với đầu và đuôi
Ingo

@Tichodroma - không tôi không cần chúng được nén lại. Nhưng tôi không thể lưu trữ tất cả các tệp văn bản tách cùng một lúc. Vì vậy, tôi muốn có được phân chia đầu tiên, thực hiện công cụ với nó, sau đó xóa phân tách đầu tiên và sau đó nhận được phân chia thứ hai. Cuối cùng, xóa gz ban đầu
lên

1
@toop: Cảm ơn đã làm rõ. Lưu ý rằng nói chung là tốt hơn để chỉnh sửa câu hỏi của bạn nếu bạn muốn làm rõ nó, thay vì đưa nó vào một bình luận; theo cách đó mọi người sẽ thấy nó
sleske

Câu trả lời được chấp nhận là tốt nếu bạn chỉ muốn một phần nhỏ của khối và không biết trước chúng. Nếu bạn muốn tạo tất cả các khối cùng một lúc, các giải pháp dựa trên sự phân tách sẽ nhanh hơn rất nhiều, O (N) thay vì O (N²).
b0fh

Câu trả lời:


11

Làm thế nào để làm điều này tốt nhất phụ thuộc vào những gì bạn muốn:

  • Bạn có muốn trích xuất một phần của tập tin lớn không?
  • Hay bạn muốn tạo tất cả các phần trong một lần?

Nếu bạn muốn một phần của tập tin , ý tưởng của bạn sẽ được sử dụng gunzipheadlà đúng. Bạn có thể dùng:

gunzip -c hugefile.txt.gz | head -n 4000000

Điều đó sẽ tạo ra 4000000 dòng đầu tiên trên tiêu chuẩn - bạn có thể muốn nối thêm một đường ống khác để thực sự làm gì đó với dữ liệu.

Để có được các phần khác, bạn sẽ sử dụng kết hợp headtail, như:

gunzip -c hugefile.txt.gz | head -n 8000000 |tail -n 4000000

để có được khối thứ hai.

Có lẽ đang thực hiện một loạt các giải pháp này hoặc gunzip -c cần đủ dung lượng để toàn bộ tệp được giải nén

Không, gunzip -ckhông yêu cầu bất kỳ dung lượng đĩa nào - nó thực hiện mọi thứ trong bộ nhớ, sau đó chuyển nó ra thiết bị xuất chuẩn.


Nếu bạn muốn tạo tất cả các phần trong một lần , sẽ hiệu quả hơn khi tạo tất cả chúng bằng một lệnh duy nhất, vì sau đó tệp đầu vào chỉ được đọc một lần. Một giải pháp tốt là sử dụng split; xem câu trả lời của jim mcnamara để biết chi tiết.


1
Từ chế độ xem hiệu suất: gzip có thực sự giải nén toàn bộ tập tin không? Hoặc là nó có thể "kỳ diệu" biết rằng chỉ cần 4 triệu dòng?
Alois Mahdal

3
@AloisMahdal: Thật ra, đó sẽ là một câu hỏi riêng biệt hay :-). Phiên bản ngắn: gzipkhông biết về giới hạn (xuất phát từ một quy trình khác). Nếu headđược sử dụng, headsẽ thoát khi nhận đủ và điều này sẽ lan truyền đến gzip(thông qua SIGPIPE, xem Wikipedia). Đối với tailđiều này là không thể, vì vậy, gzipsẽ giải nén mọi thứ.
sleske

Nhưng nếu bạn quan tâm, bạn thực sự nên hỏi điều này như một câu hỏi riêng biệt.
sleske

20

ống để phân chia sử dụng gunzip -c hoặc zcat để mở tệp

gunzip -c bigfile.gz | split -l 400000

Thêm thông số kỹ thuật đầu ra cho lệnh split.


3
Điều này là hiệu quả hơn nhiều so với câu trả lời được chấp nhận, trừ khi bạn chỉ yêu cầu một phần của các phần tách. Xin hãy ủng hộ.
b0fh

1
@ b0fh: Vâng, bạn nói đúng. Nâng cao và tham chiếu trong câu trả lời của tôi :-).
sleske

Câu trả lời tốt nhất cho chắc chắn.
Stephen Blum

các thông số kỹ thuật đầu ra là gì để các đầu ra là các tệp .gz?
Quetzalcoatl

7

Khi bạn đang làm việc trên một luồng (không thể tua lại), bạn sẽ muốn sử dụng dạng đuôi '+ N' để có được các dòng bắt đầu từ dòng N trở đi.

zcat hugefile.txt.gz | head -n 40000000
zcat hugefile.txt.gz | tail -n +40000001 | head -n 40000000
zcat hugefile.txt.gz | tail -n +80000001 | head -n 40000000


3

Chia trực tiếp tệp .gz thành các tệp .gz:

zcat bigfile.gz | split -l 400000 --filter='gzip > $FILE.gz'

Tôi nghĩ đây là điều OP muốn, vì anh ấy không có nhiều không gian.


2

Đây là một tập lệnh python để mở một tập hợp các tập tin toàn cầu từ một thư mục, gunzip chúng nếu cần thiết và đọc từng dòng một. Nó chỉ sử dụng không gian cần thiết trong bộ nhớ để giữ tên tệp và dòng hiện tại, cộng với một chút chi phí.

#!/usr/bin/env python
import gzip, bz2
import os
import fnmatch

def gen_find(filepat,top):
    for path, dirlist, filelist in os.walk(top):
        for name in fnmatch.filter(filelist,filepat):
            yield os.path.join(path,name)

def gen_open(filenames):
    for name in filenames:
        if name.endswith(".gz"):
            yield gzip.open(name)
        elif name.endswith(".bz2"):
            yield bz2.BZ2File(name)
        else:
            yield open(name)

def gen_cat(sources):
    for s in sources:
        for item in s:
            yield item

def main(regex, searchDir):
    fileNames = gen_find(regex,searchDir)
    fileHandles = gen_open(fileNames)
    fileLines = gen_cat(fileHandles)
    for line in fileLines:
        print line

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Search globbed files line by line', version='%(prog)s 1.0')
    parser.add_argument('regex', type=str, default='*', help='Regular expression')
    parser.add_argument('searchDir', , type=str, default='.', help='list of input files')
    args = parser.parse_args()
    main(args.regex, args.searchDir)

Lệnh in dòng sẽ gửi mọi dòng đến std out, vì vậy bạn có thể chuyển hướng đến một tệp. Ngoài ra, nếu bạn cho chúng tôi biết những gì bạn muốn thực hiện với các dòng, tôi có thể thêm nó vào tập lệnh python và bạn sẽ không cần phải để lại các tập tin nằm xung quanh.


2

Đây là một chương trình perl có thể được sử dụng để đọc stdin và phân chia các dòng, chuyển từng cụm thành một lệnh riêng biệt có thể sử dụng biến shell $ SPLIT để định tuyến đến đích khác. Đối với trường hợp của bạn, nó sẽ được gọi với

zcat hugefile.txt.gz | perl xsplit.pl 40000000 'cat > tmp$SPLIT.txt; do_something tmp$SPLIT.txt; rm tmp$SPLIT.txt'

Xin lỗi, quá trình xử lý dòng lệnh hơi ít nhưng bạn hiểu ý.

#!/usr/bin/perl -w
#####
# xsplit.pl: like xargs but instead of clumping input into each command's args, clumps it into each command's input.
# Usage: perl xsplit.pl LINES 'COMMAND'
# where: 'COMMAND' can include shell variable expansions and can use $SPLIT, e.g.
#   'cat > tmp$SPLIT.txt'
# or:
#   'gzip > tmp$SPLIT.gz'
#####
use strict;

sub pipeHandler {
    my $sig = shift @_;
    print " Caught SIGPIPE: $sig\n";
    exit(1);
}
$SIG{PIPE} = \&pipeHandler;

my $LINES = shift;
die "LINES must be a positive number\n" if ($LINES <= 0);
my $COMMAND = shift || die "second argument should be COMMAND\n";

my $line_number = 0;

while (<STDIN>) {
    if ($line_number%$LINES == 0) {
        close OUTFILE;
        my $split = $ENV{SPLIT} = sprintf("%05d", $line_number/$LINES+1);
        print "$split\n";
        my $command = $COMMAND;
        open (OUTFILE, "| $command") or die "failed to write to command '$command'\n";
    }
    print OUTFILE $_;
    $line_number++;
}

exit 0;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.