यूनिक्स - लाइन द्वारा एक बड़ी .gz फ़ाइल को विभाजित करें


16

मुझे यकीन है कि किसी को नीचे की आवश्यकता थी, लाइन द्वारा एक बड़ी .gz फ़ाइल को विभाजित करने का एक त्वरित तरीका क्या है? अंतर्निहित पाठ फ़ाइल में 120million पंक्तियाँ हैं। मेरे पास एक बार में पूरी फ़ाइल को गनज़िप करने के लिए पर्याप्त डिस्क स्थान नहीं है इसलिए मैं सोच रहा था कि क्या कोई व्यक्ति किसी bash / perl स्क्रिप्ट या टूल के बारे में जानता है जो फ़ाइल (या तो .gz या इनर .txt) को 3x 40mn लाइन फ़ाइलों में विभाजित कर सकता है। । यानी इसे कॉल करना:

    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 हैवीफ़ाइल। Txt.gz | सिर 4000000

नोट: मुझे अतिरिक्त डिस्क नहीं मिल सकती है।

धन्यवाद!


1
क्या आप चाहते हैं कि परिणामी फाइलें फिर से गज़ब की हों?

आप एक ipe में gunzip का उपयोग कर सकते हैं। बाकी सिर और पूंछ के साथ किया जा सकता है
इंगो

@ टिशोक्रोमा - नहीं, मुझे फिर से गज़िप करने की ज़रूरत नहीं है। लेकिन मैं एक ही बार में सभी विभाजित पाठ फ़ाइलों को संग्रहीत नहीं कर सका। इसलिए मैं चाहूंगा कि पहला विभाजन प्राप्त करें, इसके साथ सामान करें, फिर पहले विभाजन को हटाएं, और फिर दूसरा विभाजन प्राप्त करें। आखिरकार मूल gz को हटा दें
'11

1
@toop: स्पष्टीकरण के लिए धन्यवाद। ध्यान दें कि आम तौर पर अपने प्रश्न को संपादित करना बेहतर है यदि आप इसे स्पष्ट करना चाहते हैं, बजाय इसे टिप्पणी में डालना; इस तरह हर कोई इसे देखेगा।
सालेके

स्वीकृत जवाब अच्छा है यदि आप केवल हिस्सा का एक हिस्सा चाहते हैं, और उन्हें पहले से नहीं जानते हैं। यदि आप एक ही बार में सभी विखंडन उत्पन्न करना चाहते हैं, तो विभाजन पर आधारित समाधान O (N of) के बजाय O (N) बहुत तेज़ी से होंगे।
b0fh

जवाबों:


11

यह कैसे करना है यह इस बात पर निर्भर करता है कि आप क्या चाहते हैं:

  • क्या आप बड़ी फ़ाइल का एक हिस्सा निकालना चाहते हैं?
  • या क्या आप सभी भागों को एक बार में बनाना चाहते हैं?

यदि आप फ़ाइल का एक हिस्सा चाहते हैं , तो आपका उपयोग करने का विचार gunzipऔर headसही है। आप उपयोग कर सकते हैं:

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

यह पहली 4000000 लाइनों को मानक आउट पर आउटपुट करेगा - आप शायद किसी अन्य पाइप को वास्तव में डेटा के साथ कुछ करना चाहते हैं।

अन्य भागों को प्राप्त करने के लिए, आप जैसे headऔर के संयोजन का उपयोग करेंगे tail:

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

दूसरा ब्लॉक पाने के लिए।

शायद इन समाधानों की एक श्रृंखला कर रहा है या क्या गनज़िप-सी को पूरी फ़ाइल को अनज़िप करने के लिए पर्याप्त जगह की आवश्यकता होगी

नहीं, gunzip -cकिसी डिस्क स्थान की आवश्यकता नहीं है - यह मेमोरी में सब कुछ करता है, फिर इसे स्टडआउट करने के लिए स्ट्रीम करता है।


यदि आप सभी भागों को एक बार में बनाना चाहते हैं , तो उन सभी को एक ही आदेश के साथ बनाना अधिक कुशल है, क्योंकि तब इनपुट फ़ाइल केवल एक बार पढ़ी जाती है। एक अच्छा समाधान का उपयोग करना है split; विवरण के लिए jim mcnamara का उत्तर देखें।


1
प्रदर्शन के नज़रिए से: क्या वास्तव में पूरी फ़ाइल अनज़िप होती है? या क्या यह "जादुई रूप से" पता है कि केवल 4mn लाइनों की आवश्यकता है?
अलोइस महदाल

3
@AloisMahdal: वास्तव में, यह एक अलग प्रश्न होगा :-)। लघु संस्करण: gzipसीमा के बारे में नहीं जानता (जो एक अलग प्रक्रिया से आता है)। यदि headइसका उपयोग किया जाता है, headतो यह पर्याप्त होने पर बाहर निकल जाएगा, और यह gzip(SIGPIPE के माध्यम से, विकिपीडिया देखें) को प्रचारित करेगा । इसके लिए tailयह संभव नहीं है, इसलिए हां, gzipसब कुछ कम हो जाएगा।
sleske

लेकिन अगर आप रुचि रखते हैं, तो आपको वास्तव में एक अलग प्रश्न पूछना चाहिए।
sleske

20

फ़ाइल को खोलने के लिए गनज़िप -c या ज़कात का उपयोग करने के लिए पाइप को विभाजित करें

gunzip -c bigfile.gz | split -l 400000

स्प्लिट कमांड में आउटपुट स्पेसिफिकेशन जोड़ें।


3
यह स्वीकृत उत्तर की तुलना में व्यापक रूप से अधिक कुशल है, जब तक कि आपको केवल विभाजित विखंडू के एक अंश की आवश्यकता न हो। कृपया upvote
b0fh

1
@ b0fh: हां, आप सही हैं। मेरे जवाब में उकेरा गया, और संदर्भित :-)।
सिल्के

यकीन के लिए सबसे अच्छा जवाब।
स्टीफन ब्लम

आउटपुट स्पेक्स क्या हैं ताकि आउटपुट .gz फाइलें खुद हों?
क्वेटज़लकोटल

7

जब आप एक (गैर-रिवाइंड करने योग्य) स्ट्रीम पर काम कर रहे होते हैं, तो आप लाइन N से शुरू होने वाली लाइनों को प्राप्त करने के लिए पूंछ के '+ N' रूप का उपयोग करना चाहेंगे।

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

सीधे .gz फ़ाइल .gz फ़ाइलों में विभाजित करें:

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

मुझे लगता है कि यह वही है जो ओपी चाहता था, क्योंकि उसके पास ज्यादा जगह नहीं है।


2

यहां एक निर्देशिका से फ़ाइलों का एक ग्लोब सेट खोलने के लिए एक अजगर स्क्रिप्ट है, यदि आवश्यक हो, तो उन्हें गनज़िप करें और उनके माध्यम से लाइन द्वारा पढ़ें। यह केवल फ़ाइल नाम रखने के लिए मेमोरी में आवश्यक स्थान का उपयोग करता है, और वर्तमान लाइन, प्लस थोड़ा उपरि।

#!/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)

प्रिंट लाइन कमांड प्रत्येक लाइन को std आउट करने के लिए भेज देगा, जिससे आप किसी फ़ाइल पर रीडायरेक्ट कर सकते हैं। वैकल्पिक रूप से, यदि आप यह जानते हैं कि आप लाइनों के साथ क्या चाहते हैं, तो मैं इसे पाइथन लिपि में जोड़ सकता हूं और आपको फाइल को इधर-उधर रखने की जरूरत नहीं होगी।


2

यहां एक पर्ल प्रोग्राम है जिसका उपयोग स्टड को पढ़ने के लिए और लाइनों को विभाजित करने के लिए किया जा सकता है, प्रत्येक क्लैंप को एक अलग कमांड में पाइप कर सकता है जो शेल चार्ट $ SPLIT का उपयोग करके इसे एक अलग गंतव्य पर ले जा सकता है। आपके मामले के लिए, इसे लागू किया जाएगा

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

क्षमा करें, कमांड-लाइन प्रोसेसिंग थोड़ी टेढ़ी-मेढ़ी है लेकिन आपको इसका अंदाजा है।

#!/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;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.