एक निर्देशिका में 10 मिलियन से अधिक फ़ाइलों पर sed कैसे चलाएं?


16

मेरे पास एक निर्देशिका है जिसमें 10144911 फाइलें हैं। अब तक मैंने निम्नलिखित कोशिश की है:

  • for f in ls; do sed -i -e 's/blah/blee/g' $f; done

मेरे खोल को lsतोड़ दिया, एक टिल्डा में है, लेकिन मैं यह पता नहीं लगा सकता कि कैसे बनाऊं।

  • ls | xargs -0 sed -i -e 's/blah/blee/g'

बहुत अधिक के लिए args sed

  • find . -name "*.txt" -exec sed -i -e 's/blah/blee/g' {} \;

कोई और अधिक मेमोरी कांटा नहीं कर सका

इस प्रकार की कमांड बनाने के बारे में कोई अन्य विचार? फ़ाइलों को एक दूसरे के साथ संवाद करने की आवश्यकता नहीं है। ls | wc -lकाम करने के लिए लगता है (बहुत धीमी गति से) तो यह संभव होना चाहिए।


1
यदि आप sedप्रत्येक फ़ाइल के लिए इनवॉइस करने से बच सकते हैं तो यह तेज़ होगा । मुझे यकीन नहीं है कि अगर फ़ाइलों की एक श्रृंखला को खोलने, संपादित करने, सहेजने और बंद करने का कोई तरीका है sed; यदि गति आवश्यक है, तो आप एक अलग कार्यक्रम, शायद पर्ल या अजगर का उपयोग करना चाहते हैं।
intuited

@intuited: यह फ़ाइलों के लिए कुछ भी नहीं करने के लिए और भी तेजी से होगा ... गंभीरता से? यदि आप फ़ाइलों के एक सेट में एक पैटर्न बदलना चाहते हैं, तो आपको प्रत्येक फ़ाइल को देखना होगा, यदि कोई पैटर्न है। यदि आप पहले से जानते हैं कि आप 'कुछ' फ़ाइलों को छोड़ सकते हैं, तो इसका स्पष्ट रूप से तेजी से फ़ाइलों को छूने के लिए भी नहीं। और sedशुरू करने के लिए स्टार्टअप समय शायद pythonया तो तेजी से है perl, इसके अलावा अगर आप उस दुभाषिया में सब कुछ करते हैं
अकीरा

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

आपकी पहली टिप्पणी यह ​​नहीं दर्शाती है कि आप वास्तव में क्या कहना चाहते थे: "पाइथन या पर्ल प्रति पर्ल को बदलें" .. बस ऐसा करने से और कमांडलाइन ओपी ने @ देखकर, एक निर्दोष पाठक मान सकता है कि "मिल जाए। -exec python"। तेजी से "खोजने के लिए -सेक्स sed" .. जो स्पष्ट रूप से मामला नहीं है। अपने स्वयं के उत्तर में आप अजगर को अधिक बार कहते हैं कि वास्तव में इसकी आवश्यकता है।
अकीरा

मुझे लगता है कि अकीरा ने आपके (अंतर्ज्ञान) सुझाव की गलत व्याख्या की। मेरा मानना ​​है कि आप एक साथ फाइलों को बाँधने का सुझाव दे रहे थे। मैंने कोशिश की कि मेरे xargs प्रयास के साथ, इसे फिर से आज़माने का समय :)
सैंड्रो

जवाबों:


19

इसे आजमाइए:

find -name '*.txt' -print0 | xargs -0 -I {} -P 0 sed -i -e 's/blah/blee/g' {}

यह प्रत्येक आह्वान को केवल एक फ़ाइल नाम देगा sed। यह "सेड के लिए बहुत सारे आर्गन्स" समस्या को हल करेगा। -Pविकल्प कई प्रक्रियाओं एक ही समय में काँटेदार होने की अनुमति चाहिए। यदि 0 काम नहीं करता है (यह जितना संभव हो उतना चलना चाहिए), संख्या को सीमित करने के लिए अन्य संख्या (10! 100; आपके पास कोर की संख्या?) का प्रयास करें।


3
संभवतः, find . -name \*.txt -print0शेल को ग्लोब का विस्तार करने और खोजने के लिए 10 मिलियन तर्कों के लिए स्थान आवंटित करने की कोशिश करने से बचने की आवश्यकता होगी ।
क्रिस जॉन्सन

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

अब यह कोशिश कर रहा है ... उंगलियों को पार करता है
सैंड्रो

7

मैंने 10 मिलियन (खाली) फाइलों पर इस विधि (और अन्य सभी) का परीक्षण किया है , जिसका नाम "हेल्लो 00000001" है जिसे "हैलो 10000000" (14 बाइट्स प्रति नाम)।

अद्यतन: मैंने अब विधि पर एक क्वाड-कोर रन 'find |xargs'(अभी भी 'सेड' के बिना शामिल किया है; बस गूंज> / देव / अशक्त)।

# Step 1. Build an array for 10 million files
#   * RAM usage approx:  1.5 GiB 
#   * Elapsed Time:  2 min 29 sec 
  names=( hello\ * )

# Step 2. Process the array.
#   * Elapsed Time:  7 min 43 sec
  for (( ix=0, cnt=${#names[@]} ; ix<$cnt; ix++ )) ; do echo "${names[ix]}" >/dev/null ; done  

यहाँ ऊपर दिए गए परीक्षण डेटा के खिलाफ चलाने के दौरान प्रदान किए गए उत्तर कैसे दिए गए हैं, इसका सारांश दिया गया है। इन परिणामों में केवल मूल ओवरहेड्स शामिल हैं; यानी 'सेड' नहीं कहा जाता था। Sed प्रक्रिया लगभग निश्चित रूप से सबसे अधिक समय लेने वाली होगी, लेकिन मुझे लगा कि यह देखना दिलचस्प होगा कि नंगे तरीकों की तुलना कैसे की जाती है।

डेनिस की 'find |xargs'विधि, एक कोर का उपयोग करते हुए, * 4 घंटे 21 मिनट ** bash arrayएक no sedरन पर विधि की तुलना में अधिक समय लगा ... हालांकि, 'खोज' द्वारा पेश किए गए मल्टी-कोर लाभ को उन समय के अंतरों से बाहर निकलना चाहिए, जब sed के लिए बुलाया जा रहा है। फ़ाइलों को संसाधित कर रहा है ...

           | Time    | RAM GiB | Per loop action(s). / The command line. / Notes
-----------+---------+---------+----------------------------------------------------- 
Dennis     | 271 min | 1.7 GiB | * echo FILENAME >/dev/null
Williamson   cores: 1x2.66 MHz | $ time find -name 'hello *' -print0 | xargs -0 -I {} echo >/dev/null {}
                               | Note: I'm very surprised at how long this took to run the 10 million file gauntlet
                               |       It started processing almost immediately (because of xargs I suppose),  
                               |       but it runs **significantly slower** than the only other working answer  
                               |       (again, probably because of xargs) , but if the multi-core feature works  
                               |       and I would think that it does, then it could make up the defecit in a 'sed' run.   
           |  76 min | 1.7 GiB | * echo FILENAME >/dev/null
             cores: 4x2.66 MHz | $ time find -name 'hello *' -print0 | xargs -0 -I {} -P 0 echo >/dev/null {}
                               |  
-----------+---------+---------+----------------------------------------------------- 
fred.bear  | 10m 12s | 1.5 GiB | * echo FILENAME >/dev/null
                               | $ time names=( hello\ * ) ; time for (( ix=0, cnt=${#names[@]} ; ix<$cnt; ix++ )) ; do echo "${names[ix]}" >/dev/null ; done
-----------+---------+---------+----------------------------------------------------- 
l0b0       | ?@#!!#  | 1.7 GiB | * echo FILENAME >/dev/null 
                               | $ time  while IFS= read -rd $'\0' path ; do echo "$path" >/dev/null ; done < <( find "$HOME/junkd" -type f -print0 )
                               | Note: It started processing filenames after 7 minutes.. at this point it  
                               |       started lots of disk thrashing.  'find' was using a lot of memory, 
                               |       but in its basic form, there was no obvious advantage... 
                               |       I pulled the plug after 20 minutes.. (my poor disk drive :(
-----------+---------+---------+----------------------------------------------------- 
intuited   | ?@#!!#  |         | * print line (to see when it actually starts processing, but it never got there!)
                               | $ ls -f hello * | xargs python -c '
                               |   import fileinput
                               |   for line in fileinput.input(inplace=True):
                               |       print line ' 
                               | Note: It failed at 11 min and approx 0.9 Gib
                               |       ERROR message: bash: /bin/ls: Argument list too long  
-----------+---------+---------+----------------------------------------------------- 
Reuben L.  | ?@#!!#  |         | * One var assignment per file
                               | $ ls | while read file; do x="$file" ; done 
                               | Note: It bombed out after 6min 44sec and approx 0.8 GiB
                               |       ERROR message: ls: memory exhausted
-----------+---------+---------+----------------------------------------------------- 


1

यह ज्यादातर ऑफ-टॉपिक है, लेकिन आप इसका उपयोग कर सकते हैं

find -maxdepth 1 -type f -name '*.txt' | xargs python -c '
import fileinput
for line in fileinput.input(inplace=True):
    print line.replace("blah", "blee"),
'

यहां मुख्य लाभ (ओवर ... xargs ... -I {} ... sed ...) गति है: आप sed10 मिलियन बार निवेश करने से बचते हैं । यह तब भी तेज़ होगा जब आप पायथन का उपयोग करने से बच सकते थे (चूँकि अजगर धीमे, अपेक्षाकृत प्रकार का है), इसलिए पर्ल इस कार्य के लिए बेहतर विकल्प हो सकता है। मुझे यकीन नहीं है कि पर्ल के साथ आसानी से समकक्ष कैसे किया जाए।

जिस तरह से यह काम करता है, वह xargsपायथन को कई तर्कों के साथ आमंत्रित करेगा, क्योंकि यह एक ही कमांड लाइन पर फिट हो सकता है, और ऐसा तब तक करता रहेगा जब तक कि यह तर्कों से बाहर नहीं निकलता (जो कि आपूर्ति की जा रही है ls -f *.txt)। प्रत्येक आह्वान के तर्कों की संख्या फिलानेम की लंबाई और um, कुछ अन्य सामान पर निर्भर करेगी। fileinput.inputसमारोह प्रत्येक मंगलाचरण के तर्कों में नामित फ़ाइलों से लगातार लाइनों पैदावार, और inplaceविकल्प यह जादुई "पकड़" उत्पादन के लिए और इसका इस्तेमाल प्रत्येक पंक्ति को बदलने के लिए कहता है।

ध्यान दें कि पायथन की स्ट्रिंग replaceविधि regexps का उपयोग नहीं करती है; यदि आपको जरूरत है, तो आपको इनका import reउपयोग करना होगा print re.sub(line, "blah", "blee")। वे पर्ल-कम्पेटिबल रेग्जैम्प्स हैं, जो आपके साथ मिलने वाले भारी फोर्टिफाइड संस्करणों की तरह हैं sed -r

संपादित करें

जैसा कि अकीरा ने टिप्पणी में उल्लेख किया है, कमांड के ls -f *.txtस्थान पर ग्लोब ( ) का उपयोग करने वाला मूल संस्करण findकाम नहीं करेगा क्योंकि ग्लब्स को शेल ( bash) द्वारा ही संसाधित किया जाता है। इसका मतलब यह है कि कमांड चलाने से पहले, 10 मिलियन फ़ाइलनाम को कमांड लाइन में प्रतिस्थापित किया जाएगा। यह कमांड के तर्क सूची के अधिकतम आकार को पार करने के लिए बहुत अधिक गारंटी है। आप xargs --show-limitsइस पर सिस्टम-विशिष्ट जानकारी के लिए उपयोग कर सकते हैं ।

तर्क सूची के अधिकतम आकार को भी ध्यान में रखा जाता है xargs, जो उस सीमा के अनुसार अजगर के प्रत्येक आह्वान को पारित करने वाले तर्कों की संख्या को सीमित करता है। चूँकि xargsअभी भी कुछ समय के लिए अजगर को आमंत्रित करना होगा, अकीरा os.path.walkको फाइल लिस्टिंग प्राप्त करने के लिए उपयोग करने का सुझाव शायद आपको कुछ समय बचाएगा।


1
ग्लोब ऑपरेटर का उपयोग करने का क्या मतलब है (जो वैसे भी कई फ़ाइलों के लिए विफल हो जाएगा) ... और फिर उन फ़ाइलों को अजगर को खिलाएं जिनके पास है os.path.walk()?
अकीरा

@akira: ग्लोब ऑपरेटर की सामग्री को बदलने की कोशिश कर रहा से बचने के लिए है .और ..। निश्चित रूप से ऐसा करने के अन्य तरीके हैं (यानी find), लेकिन मैं ओपी को समझने के लिए जितना संभव हो उतना करीब से कोशिश कर रहा हूं। उपयोग न करने का कारण भी यही है os.path.walk
intuited

@akira: अच्छा सुझाव, हालांकि, यह शायद काफी तेज होगा।
intuited

मुझे लगता है कि ओपी os.path.walkकाफी आसानी से समझ जाएगा ।
अकीरा

0

प्रयत्न:

ls | while read file; do (something to $file); done

2
ls -fबेहतर होगा; क्या आप वास्तव में इसके लिए प्रतीक्षा करना चाहते हैं stat()और कई फ़ाइलों को सॉर्ट करना चाहते हैं ?
गाइकोसॉर

अभी मैं कोशिश कर रहा हूँ: f में * .txt के लिए; ब्लाह करना; किया हुआ। अगर वह विफल हो जाता है तो मैं उसे एक झटका दूंगा। धन्यवाद!
सैंड्रो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.