2009-09-11 16 views
37

दिया गया: एक 'विशेष' पहली पंक्ति (उदाहरण के लिए, फ़ील्ड नाम) के साथ एक बड़ी टेक्स्ट-डेटा फ़ाइल (उदा। सीएसवी प्रारूप)।फ़ाइल को कैसे विभाजित करें और प्रत्येक पंक्ति में पहली पंक्ति को रखें?

तलाश है: coreutils split -l आदेश, लेकिन अतिरिक्त आवश्यकता यह है कि मूल फ़ाइल से शीर्ष लेख पंक्ति जिसके परिणामस्वरूप टुकड़े में से प्रत्येक की शुरुआत में दिखाई साथ की एक बराबर।

मुझे लगता है कि split और head के कुछ संकोचन का अनुमान लगाया जाएगा?

+8

ऐसा नहीं है कि किसी को उस जोड़ना चाहिए एक अंतर्निहित की सुविधा 'split' के रूप में उचित लगता है है ना? –

+1

शायद यह सबसे बड़ा कारक * * बनने के खिलाफ * यह है कि आप आम तौर पर 'बिल्ली ए बी सी> पुनर्निर्मित' करके एक विभाजित फ़ाइल का पुनर्निर्माण करते हैं। फ़ाइल में अतिरिक्त रेखाओं का मतलब है कि सामान्य पुनर्निर्माण दृष्टिकोण मूल फ़ाइल को पुन: पेश नहीं करता है। –

+2

आगामी (* नहीं *) "' unsplit --remove-header' "उपयोगिता यही है! लेकिन गंभीरता से, 'विभाजित', यदि यह "दोहराव-हेडर" विकल्प था, तो अभी भी अपने वर्तमान व्यवहार के लिए डिफ़ॉल्ट होना चाहिए। यदि आप वास्तव में चाहते थे तो आप केवल हेडर सामान का उपयोग करेंगे। –

उत्तर

32

यह robhruska के स्क्रिप्ट थोड़ा साफ किया जाता है:

tail -n +2 file.txt | split -l 4 - split_ 
for file in split_* 
do 
    head -n 1 file.txt > tmp_file 
    cat $file >> tmp_file 
    mv -f tmp_file $file 
done 

मैं wc, cut, ls और स्थानों पर जहां वे अनावश्यक हैं में echo हटा दिया। मैंने कुछ फाइलनामों को थोड़ा और सार्थक बनाने के लिए बदल दिया। मैंने इसे पढ़ने के लिए आसान बनाने के लिए केवल कई लाइनों पर तोड़ दिया।

यदि आप फैंसी प्राप्त करना चाहते हैं, तो आप हार्ड कोड किए गए एक का उपयोग करने के बजाय अस्थायी फ़ाइल नाम बनाने के लिए mktemp या tempfile का उपयोग कर सकते हैं।

संपादित

जीएनयू split का उपयोग करते हुए यह यह करने के लिए संभव है:

split_filter() { { head -n 1 file.txt; cat; } > "$FILE"; }; export -f split_filter; tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_ 

पठनीयता के लिए बाहर टूटी:

split_filter() { { head -n 1 file.txt; cat; } > "$FILE"; } 
export -f split_filter 
tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_ 

जब --filter निर्दिष्ट किया जाता है, split आदेश रन (एक प्रत्येक आउटपुट फ़ाइल के लिए इस मामले में फ़ंक्शन, जिसे निर्यात किया जाना चाहिए) v सेट करता है आवेदक FILE, कमांड के पर्यावरण में, फ़ाइल नाम पर।

एक फ़िल्टर स्क्रिप्ट या फ़ंक्शन आउटपुट सामग्री या यहां तक ​​कि फ़ाइल नाम के लिए इच्छित किसी भी मैनिपुलेशन को कर सकता है। उत्तरार्द्ध का एक उदाहरण एक चर निर्देशिका में एक निश्चित फ़ाइल नाम में आउटपुट हो सकता है: उदाहरण के लिए > "$FILE/data.dat"

+0

यह निश्चित रूप से काम करेगा। मैं बस कुछ स्लिम एक-लाइनर की उम्मीद कर रहा था जैसे '$ $ भाग के लिए (split -l 1000 myfile); बिल्ली <(head -n1 myfile) $ part> myfile। $ part; किया गया ' – Arkady

+0

यह काम नहीं कर सकता क्योंकि आवश्यकता के' विभाजन ', 'stdout' पर आउटपुट नहीं है। –

+0

'split' * * फ़ाइलों के * नाम * को stdout करने के लिए आउटपुट कर सकता है, हालांकि (जब तक हम चर्चा कर रहे हैं कि 'split' * चाहिए * क्या करना है :-) – Arkady

4

जब मैं बैश-फु की बात करता हूं तो मैं नौसिखिया हूं, लेकिन मैं इस दो-कमांड राक्षसों को समझने में सक्षम था। मुझे यकीन है कि और अधिक सुरुचिपूर्ण समाधान हैं।

$> tail -n +2 file.txt | split -l 4 
$> for file in `ls xa*`; do echo "`head -1 file.txt`" > tmp; cat $file >> tmp; mv -f tmp $file; done 

यह मानते हुए है अपने इनपुट फ़ाइल file.txt है, तो आप split करने के लिए prefix तर्क का उपयोग नहीं कर रहे हैं, और आप एक निर्देशिका में काम कर रहे हैं कि कोई अन्य फ़ाइल split की डिफ़ॉल्ट के साथ शुरू नहीं है xa* आउटपुट प्रारूप। साथ ही, अपने वांछित विभाजन लाइन आकार के साथ '4' को प्रतिस्थापित करें।

1

मुझे अन्य लोगों की साइटों से सीधे स्क्रिप्ट कॉपी करने के नियमों के बारे में निश्चित नहीं है, लेकिन Geekology में आप जो चाहते हैं उसे करने के लिए एक अच्छी स्क्रिप्ट है, कुछ टिप्पणियों के साथ यह पुष्टि करता है कि यह काम करता है। tail-n+2 करना सुनिश्चित करें कि नीचे के पास एक टिप्पणी में उल्लेख किया गया है।

2

यह डेनिस विलियमसन की एक और अधिक मजबूत संस्करण है। लिपि बहुत सारी अस्थायी फाइलें बनाती है, और अगर शर्मनाक था तो अगर वे चारों ओर झूठ बोल रहे थे तो यह शर्म की बात होगी। तो, चलिए सिग्नल फँसाना जोड़ें (http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html और फिर http://tldp.org/LDP/abs/html/debugging.html देखें) और हमारी अस्थायी फ़ाइलों को हटा दें; वैसे भी यह एक अच्छा अभ्यास है।

trap 'rm split_* tmp_file ; exit 13' SIGINT SIGTERM SIGQUIT 
tail -n +2 file.txt | split -l 4 - split_ 
for file in split_* 
do 
    head -n 1 file.txt > tmp_file 
    cat $file >> tmp_file 
    mv -f tmp_file $file 
done 

जो भी रिटर्न कोड आप चाहते हैं उसके साथ '13' बदलें। ओह, और आपको शायद mktemp का उपयोग करना चाहिए (जैसा कि कुछ पहले ही सुझाए गए हैं), इसलिए आगे बढ़ें और जाल लाइन में आरएम से 'tmp_file' को हटा दें।

8

आप को सिग्नल मैन पेज देखें उपयोग कर सकते हैं [मिलीग्राम] awk:

awk 'NR==1{ 
     header=$0; 
     count=1; 
     print header > "x_" count; 
     next 
    } 

    !((NR-1) % 100){ 
     count++; 
     print header > "x_" count; 
    } 
    { 
     print $0 > "x_" count 
    }' file 

100 प्रत्येक टुकड़ा की लाइनों की संख्या है यह अस्थायी फ़ाइलों की आवश्यकता नहीं है और एक ही लाइन पर डाला जा सकता

+0

मुझे कुछ नया सिखाने के लिए उपवास, लेकिन अगर मैं एक छोटी लिपि लिखने जा रहा हूं, तो मैं इसे पर्ल या पायथन में भी कर सकता हूं :-) – Arkady

5

आप नए इस्तेमाल कर सकते हैं।। - जीएनयू कोर्यूटल्स में फ़िल्टर कार्यक्षमता> = 8.13 (2011):

tail -n +2 FILE.in | 
split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"' 
+1

मुझे एक-लाइनर संस्करण पसंद है। बस इसे बाश के लिए अधिक सामान्य बनाने के लिए, मैंने किया: 'tail -n +2 FILE.in | split -d -lines 50 - --filter = 'bash -c "{head -n1 $ {FILE%। *}; बिल्ली;}> $ FILE"' FILE.in.x' – KullDox

1

मैं जहां आप आसानी से दानेदार के रूप में विभाजित अंश निर्दिष्ट कर सकते हैं मार्को की awk संस्करण, इस एक सरल एक लाइनर से अपनाया पसंद आया के रूप में आप चाहते हैं:

awk 'NR==1{print $0 > FILENAME ".split1"; print $0 > FILENAME ".split2";} NR>1{if (NR % 10 > 5) print $0 >> FILENAME ".split1"; else print $0 >> FILENAME ".split2"}' file 
+0

मुझे यह समाधान पसंद है, हालांकि यह केवल दो स्प्लिट फाइलों तक ही सीमित है – Bas

+0

यदि आपको यह पसंद है तो इसके लिए अपवॉट सुविधा है;) इसे आसानी से अधिक फ़ाइलों में समायोजित किया जा सकता है, लेकिन हां यह split -l – DreamFlasher

+0

"एक लाइनर" के रूप में लचीला नहीं है ... pshh – Pandem1c

1

मैं वास्तव में रोब और डेनिस के संस्करणों पसंद आया, इतना है कि मैं उन्हें सुधारना चाहता था।

in_file=$1 
awk '{if (NR!=1) {print}}' $in_file | split -d -a 5 -l 100000 - $in_file"_" # Get all lines except the first, split into 100,000 line chunks 
for file in $in_file"_"* 
do 
    tmp_file=$(mktemp $in_file.XXXXXX) # Create a safer temp file 
    head -n 1 $in_file | cat - $file > $tmp_file # Get header from main file, cat that header with split file contents to temp file 
    mv -f $tmp_file $file # Overwrite non-header containing file with header-containing file 
done 

अंतर::

यहाँ मेरी संस्करण है

  1. in_file फ़ाइल तर्क है आप बेहतर प्रदर्शन
  2. होने को बनाए रखने के हेडर
  3. उपयोग awk बजाय tail विभाजित करने के लिए awk की वजह से चाहते हैंकी बजाय 100,000 लाइन फ़ाइलों में विभाजित (- "-d -एक 5" विभाजन तर्क से 99999 तक)
  4. mktemp उपयोग सुरक्षित रूप से अस्थायी फ़ाइलों को संभालने के लिए
  5. उपयोग एकल head | cat लाइन
  6. स्प्लिट फ़ाइल नाम इनपुट फ़ाइल नाम एक अंडरस्कोर और संख्या के साथ संलग्न किया जाएगा के बजाय दो पंक्तियों
0

उपयोग जीएनयू समानांतर:

parallel -a bigfile.csv --header : --pipepart 'cat > {#}' 

आप भागों में से प्रत्येक पर एक कमांड को चलाने के लिए की जरूरत है, तो जीएनयू समानांतर कर मदद कर सकते हैं, भी:

+०१२३५१६४१०६१
parallel -a bigfile.csv --header : --pipepart my_program_reading_from_stdin 
parallel -a bigfile.csv --header : --pipepart --fifo my_program_reading_from_fifo {} 
parallel -a bigfile.csv --header : --pipepart --cat my_program_reading_from_a_file {} 

यदि आप प्रति CPU कोर 2 भागों में विभाजित करना चाहते हैं (उदा।24 कोर = 48 बराबर आकार भागों):

parallel --block -2 -a bigfile.csv --header : --pipepart my_program_reading_from_stdin 

आप 10 एमबी ब्लॉकों में विभाजित करना चाहते हैं:

parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin 
संबंधित मुद्दे