एक बड़ी फ़ाइल के बीच में पढ़ें


19

मेरे पास 1 टीबी फ़ाइल है। मैं बाइट 12345678901 से बाइट 19876543212 तक पढ़ना चाहता हूं और 100 एमबी रैम के साथ मशीन पर मानक आउटपुट देता हूं।

मैं आसानी से एक पर्ल स्क्रिप्ट लिख सकता हूं जो ऐसा करता है। sysread 700 MB / s (जो ठीक है) डिलीवर करता है, लेकिन syswrite केवल 30 MB / s डिलीवर करता है। मैं कुछ अधिक कुशल, अधिमानतः कुछ ऐसा चाहूंगा जो हर यूनिक्स प्रणाली में स्थापित हो और जो 1 जीबी / एस के क्रम में वितरित कर सके।

मेरा पहला विचार है:

dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))

लेकिन वह कुशल नहीं है।

संपादित करें:

मुझे पता नहीं है कि मैंने साइरसाइट को कैसे गलत तरीके से मापा। यह 3.5 जीबी / एस बचाता है:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
         while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
            $left -= $read; syswrite(STDOUT,$buf);
         }' 12345678901 $((19876543212-12345678901)) < bigfile

और yes | dd bs=1024k count=10 | wcदुःस्वप्न से बचा जाता है ।


के साथ अपने आदेशbs=1M iflag=skip_bytes,count_bytes
frostschutz

जवाबों:


21

यह छोटे ब्लॉक आकार के कारण धीमा है। एक हालिया GNU dd( coreutils v8.16 + ) का उपयोग करते हुए, सबसे सरल तरीका है विकल्पों skip_bytesऔर count_bytesविकल्पों का उपयोग करना :

in_file=1tb

start=12345678901
end=19876543212
block_size=4096

copy_size=$(( $end - $start ))

dd if="$in_file" iflag=skip_bytes,count_bytes,fullblock bs="$block_size" \
  skip="$start" count="$copy_size"

अपडेट करें

fullblockविकल्प @Gilles उत्तर के अनुसार ऊपर जोड़ा गया है । पहले तो मुझे लगा था कि यह आरोपित हो सकता है count_bytes, लेकिन ऐसा नहीं है।

जिन मुद्दों का उल्लेख किया गया है, वे एक संभावित समस्या है, यदि ddकिसी कारण से रीड / राइट कॉल बाधित होती है तो डेटा खो जाएगा। यह ज्यादातर मामलों में होने की संभावना नहीं है (कुछ हद तक कम हो जाता है क्योंकि हम एक फ़ाइल से पढ़ रहे हैं और पाइप नहीं है)।


ddबिना विकल्पों skip_bytesऔर count_bytesविकल्पों का उपयोग करना अधिक कठिन है:

in_file=1tb

start=12345678901
end=19876543212
block_size=4096

copy_full_size=$(( $end - $start ))
copy1_size=$(( $block_size - ($start % $block_size) ))
copy2_start=$(( $start + $copy1_size ))
copy2_skip=$(( $copy2_start / $block_size ))
copy2_blocks=$(( ($end - $copy2_start) / $block_size ))
copy3_start=$(( ($copy2_skip + $copy2_blocks) * $block_size ))
copy3_size=$(( $end - $copy3_start ))

{
  dd if="$in_file" bs=1 skip="$start" count="$copy1_size"
  dd if="$in_file" bs="$block_size" skip="$copy2_skip" count="$copy2_blocks"
  dd if="$in_file" bs=1 skip="$copy3_start" count="$copy3_size"
}

आप विभिन्न ब्लॉक आकारों के साथ भी प्रयोग कर सकते हैं, लेकिन लाभ बहुत नाटकीय नहीं होगा। देखें - क्या dd के लिए bs पैरामीटर के लिए इष्टतम मान निर्धारित करने का कोई तरीका है?


@Gememe दूसरी विधि विफल bsनहीं होगी अगर का एक कारक नहीं है skip?
स्टीवन पेनी

@StevenPenny, निश्चित नहीं है कि आपको क्या मिल रहा है, लेकिन skipकई ब्लॉक हैं, बाइट्स नहीं। हो सकता है आप उलझन में कर रहे हैं के बाद से skip_bytesपहला उदाहरण अर्थ में प्रयोग किया जाता है skip है वहाँ बाइट में?
ग्रीम

आपका मतलब bsहै 4,096, जिसका अर्थ है कि आप अधिक सटीक रूप से उस 4,096बाइट्स को नहीं छोड़ सकते
स्टीवन पेनी

1
@StevenPenny, यही कारण है कि डेटा को कॉपी करने के लिए ddपहले और आखिरी उपयोग के साथ तीन अलग-अलग रन हैं bs=1जो एक ब्लॉक संरेखण पर शुरू या खत्म नहीं करते हैं।
ग्रीम

6

bs=1ddएक बार में एक बाइट को पढ़ना और लिखना बताता है। प्रत्येक readऔर writeकॉल के लिए एक ओवरहेड है , जो इसे धीमा करता है। अच्छे प्रदर्शन के लिए एक बड़े ब्लॉक आकार का उपयोग करें।

आप एक पूरे फ़ाइल को कॉपी करते हैं, लिनक्स के तहत कम से कम, मुझे लगता है कि मिल गया है cpऔर catतेजी से कर रहे हैंdd , भले ही आप एक बड़े ब्लॉक आकार निर्दिष्ट,।

एक फ़ाइल का ही हिस्सा कॉपी करने के लिए, आप कर सकते हैं पाइप tailमें head। इसके लिए जीएनयू कोर्यूटिल्स या कुछ अन्य कार्यान्वयन की आवश्यकता होती head -cहै जिसमें निर्दिष्ट संख्या में बाइट्स की नकल करनी होती है ( tail -cजो कि POSIX में है लेकिन head -cऐसा नहीं है)। लिनक्स पर एक त्वरित मानदंड यह दिखाता ddहै कि पाइप की वजह से धीमी है ।

tail -c $((2345678901+1)) | head -c $((19876543212-2345678901))

इसके साथ समस्या यहdd है कि यह विश्वसनीय नहीं है: यह आंशिक डेटा की प्रतिलिपि बना सकता है । जहाँ तक मुझे पता है, ddएक नियमित फ़ाइल को पढ़ते और लिखते समय सुरक्षित है - देखें कि डेटा की प्रतिलिपि बनाने के लिए dd कब उपयुक्त है? (या, जब पढ़ा जाता है () और लिखना () आंशिक) - लेकिन केवल तब तक जब तक यह एक सिग्नल द्वारा बाधित नहीं होता है । जीएनयू कोर्यूटिल्स के साथ, आप fullblockध्वज का उपयोग कर सकते हैं , लेकिन यह पोर्टेबल नहीं है।

इसके साथ एक और समस्या ddयह है कि यह काम करने वाले ब्लॉक काउंट को खोजने के लिए कठिन हो सकता है, क्योंकि स्किप किए गए बाइट्स की संख्या और स्थानांतरित बाइट्स संख्या दोनों को ब्लॉक आकार के एक से अधिक होने की आवश्यकता है। आप कई कॉल का उपयोग कर सकते हैं dd: पहला आंशिक ब्लॉक कॉपी करने के लिए, एक एलाइन ब्लॉक को कॉपी करने के लिए और दूसरा आंशिक ब्लॉक कॉपी करने के लिए - एक शेल स्निपेट के लिए ग्रीम का जवाब देखें । लेकिन यह मत भूलो कि जब आप स्क्रिप्ट चलाते हैं, जब तक आप fullblockध्वज का उपयोग नहीं करते हैं , तो आपको प्रार्थना करने की आवश्यकता होती है जो ddसभी डेटा को कॉपी करेगा। ddयदि कोई प्रतिलिपि आंशिक है, तो नॉनज़रो स्थिति लौटाता है, इसलिए त्रुटि का पता लगाना आसान है, लेकिन इसे सुधारने का कोई व्यावहारिक तरीका नहीं है।

POSIX के पास शेल स्तर पर पेशकश करने के लिए कुछ भी बेहतर नहीं है। मेरी सलाह एक छोटे से विशेष प्रयोजन सी प्रोग्राम लिखने के लिए होगा (पर आपको ठीक ऐसा ही लागू निर्भर करता है, आप इसे कॉल कर सकते हैं dd_done_rightया tail_headया mini-busybox)।


वाह, मैं yes | dd bs=1024k count=10 | wcपहले कभी समस्या नहीं जानता था । बुरा।
ओले तांगे

4

के साथ dd:

dd if=1tb skip=12345678901 count=$((19876543212-12345678901)) bs=1M iflags=skip_bytes,count_bytes

वैकल्पिक रूप से losetup:

losetup --find --show --offset 12345678901 --sizelimit $((19876543212-12345678901))

और फिर dd, cat... लूप डिवाइस।


यह बहुत लिनक्स केंद्रित लगता है। मुझे AIX, FreeBSD और Solaris पर भी काम करने के लिए समान कोड की आवश्यकता है।
ओले तांगे

0

यह है कि आप यह कैसे कर सकते हैं:

   i=$(((t=19876543212)-(h=12345678901)))
   { dd count=0 skip=1 bs="$h"
     dd count="$((i/(b=64*1024)-1))" bs="$b"
     dd count=1 bs="$((i%b))"
   } <infile >outfile

यह सब वास्तव में आवश्यक है - इसे और अधिक की आवश्यकता नहीं है। पहली जगह में नियमित रूप से तुरंत इनपुट पर नियमित रूप से फ़ाइल इनपुट खत्म dd count=0 skip=1 bs=$block_size1हो जाएगा lseek()मिस्ड डेटा का कोई मौका नहीं है या इसके बारे में जो कुछ भी असत्य बताया गया है, आप बस सीधे अपनी वांछित शुरुआत की तलाश कर सकते हैं। चूँकि फ़ाइल डिस्क्रिप्टर शेल के स्वामित्व में है और ddकेवल इसे प्राप्त कर रहे हैं, वे इसकी कर्सर स्थिति को प्रभावित करेंगे और इसलिए आप इसे केवल चरणों में ले सकते हैं। यह वास्तव में बहुत सरल है - और कार्य से बेहतर कोई मानक उपकरण नहीं है dd

यह एक 64k ब्लॉक का उपयोग करता है जो अक्सर आदर्श होता है। आम धारणा के विपरीत, बड़े अवरोधक ddकाम को तेज नहीं बनाते हैं। दूसरी ओर, छोटे बफ़र्स भी अच्छे नहीं हैं। ddसिस्टम कॉल में अपने समय को सिंक्रनाइज़ करने की आवश्यकता है ताकि इसे मेमोरी में डेटा कॉपी करने और फिर से आउट करने की प्रतीक्षा न करें, बल्कि यह भी कि सिस्टम कॉल पर प्रतीक्षा करने की आवश्यकता न हो। इसलिए आप चाहते हैं कि यह पर्याप्त समय ले कि अगले read()को आखिरी इंतजार न करना पड़े, लेकिन इतना भी नहीं कि आप बड़े आकार में बफ़र कर रहे हों।

तो पहले ddस्थिति शुरू करने के लिए रुक जाती है। जिसमें शून्य समय लगता है । आप उस बिंदु पर पसंद किए गए किसी अन्य कार्यक्रम को उसके स्टड को पढ़ने के लिए कह सकते हैं और यह सीधे आपके वांछित बाइट ऑफसेट पर पढ़ना शुरू कर देगा। मैं एक और कॉल ddकरने के ((interval / blocksize) -1)लिए स्टडआउट के लिए गिनती ब्लॉक पढ़ता हूं ।

आखिरी चीज जो आवश्यक है वह पिछले डिवीजन ऑपरेशन के मापांक (यदि कोई हो) की नकल करना है। और वही जो है।

इस पर विश्वास मत करो, वैसे, जब लोग सबूत के बिना अपने चेहरे पर तथ्यों को राज्य करते हैं। हां, ddएक छोटी रीड करना संभव है (हालांकि स्वस्थ ब्लॉक डिवाइस से इस तरह की चीजें संभव नहीं हैं - इस प्रकार नाम) । ऐसी चीजें केवल तभी संभव हैं जब आप एक ddधारा को सही ढंग से बफर नहीं करते हैं जो एक ब्लॉक डिवाइस के अलावा अन्य से पढ़ी जाती है। उदाहरण के लिए:

cat data | dd bs="$num"    ### incorrect
cat data | dd ibs="$PIPE_MAX" obs="$buf_size"   ### correct

दोनों मामलों में सभी डेटा की ddप्रतिलिपि बनाता है । पहले मामले में यह संभव है (हालांकि साथ की संभावना नहीं ) है कि उत्पादन ब्लॉक जिनमें से कुछ थोड़ा बाहर इच्छा प्रतियां बराबर "$ संख्या" बाइट्स क्योंकि spec'd है केवल सब पर कुछ भी बफ़र होना जब बफर विशेष रूप से अपनी कमान पर अनुरोध किया जाता है लाइन। एक का प्रतिनिधित्व करता है अधिकतम ब्लॉक आकार क्योंकि उद्देश्य के वास्तविक समय मैं / हे है।catddddbs=dd

दूसरे उदाहरण में, मैं स्पष्ट रूप से आउटपुट को निर्दिष्ट करता हूं और ddबफ़र तब तक पढ़ता है जब तक कि पूर्ण लेखन नहीं किया जा सकता है। यह प्रभावित नहीं करता है count=जो इनपुट ब्लॉक पर आधारित है, लेकिन इसके लिए आपको बस एक और की आवश्यकता है dd। कोई गलत सूचना जो आपको दी गई है अन्यथा अवहेलना की जानी चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.