बड़े फ़ोल्डर पदानुक्रम में पाठ प्रतिस्थापन कैसे करें?


11

मैं कुछ उदाहरणों को छोड़कर फ़ाइलों के एक बड़े सेट में कुछ पाठ को खोजना और बदलना चाहता हूं। प्रत्येक पंक्ति के लिए, मुझे एक प्रॉम्प्ट चाहिए जो मुझसे पूछें कि मुझे उस लाइन को बदलने की आवश्यकता है या नहीं। Vim's :%s/from/to/gc( cपुष्टि के लिए संकेत के साथ ) के समान कुछ , लेकिन फ़ोल्डरों के एक सेट के पार। क्या कुछ अच्छी कमांड लाइन टूल या स्क्रिप्ट है जिसका उपयोग किया जा सकता है?


उचित स्वरूपण के महत्व पर: मैंने शुरू में आपके आदेश को s/from/to/gएक प्रारूपण के साथ पढ़ा होगा, उसके बाद एक स्वरूपित गड़बड़ के बजाय s/from/to/gcजिस cपर आप लिखने का प्रयास करेंगे (आप मार्कडाउन के साथ ऐसा नहीं कर सकते, आप इसे कर सकते हैं <code>और <strong>HTML टैग्स)।
गिल्स एसओ- बुराई को रोकना '

जवाबों:


19

विम का उपयोग क्यों नहीं करते?

सभी फाइलों को विम में खोलें

vim $(find . -type f)

या केवल प्रासंगिक फाइलें खोलें (जैसा कि कालेब द्वारा सुझाया गया है)

vim $(grep 'from' . -Rl)

और फिर सभी बफ़र्स में प्रतिस्थापित चलाएँ

:bufdo %s/from/to/gc | update

आप इसके साथ भी कर सकते हैं sed, लेकिन मेरा sed ज्ञान सीमित है।


धन्यवाद, आपके उत्तर ने मुझे एक लेट डबल-टेक कर दिया: मुझे एहसास हुआ कि मैं पूरी तरह से इंटरेक्टिव बिट से चूक गया हूं। मुझे नहीं लगता कि यह सेड के साथ भी संभव है (पर्याप्त इनपुट / आउटपुट चैनल नहीं)।
गिल्स एसओ- बुराई को रोकना '

1
आप नहीं का उपयोग करके वर्तमान बफर में सभी फ़ाइलों को खोलने के द्वारा इस तेजी लाने सकता है grepबजाय findजिससे कि आप केवल खुली फ़ाइलों मैचों को जानते हैं कि। vim $(grep 'from' . -Rl)
कालेब

Thanks.The (ग के आसपास astreriks) की आवश्यकता है? या इसकी स्वरूपण समस्या?
बाल्की

@balki यह एक "स्वरूपण" समस्या है। इसे निश्चित करें
गर्ट

5

आप एक छोटी सी पर्ल स्क्रिप्ट के साथ कुछ क्रूड कर सकते हैं, जिसे -l -peतर्क ( -i) के रूप में पास की गई फ़ाइलों के स्थान पर लाइन ( ) द्वारा रिप्लेसमेंट लाइन करने का निर्देश दिया गया है :

perl -i -l -pe '
    if (/from/) {                            # is the source text present on this line?
        printf STDERR ("%s: %s [y/N]? ", $ARGV, $_);  # display a prompt
        $r=<STDIN>;                                   # read user response
        if ($r =~ /^[Yy]/) {                          # if user entered Y:
            s/from/to/g;                              # replace all occurences on this line
    }' /path/to/files

संभावित सुधार, प्रॉम्प्ट के कुछ हिस्सों को रंगने और "वर्तमान फ़ाइल में सभी आवृत्तियों को प्रतिस्थापित करने" जैसी चीजों का समर्थन करने के लिए होगा। एक पंक्ति में प्रत्येक घटना के लिए अलग-अलग संकेत देना कठिन होगा।

दूसरा भाग, फाइलों का मिलान। यदि बहुत अधिक फाइलें शामिल नहीं हैं और आप zsh चला रहे हैं, तो आप वर्तमान निर्देशिका की सभी फाइलों और उसकी उपनिर्देशिकाओं की पुनरावृत्ति से मिलान कर सकते हैं:

perl -i -l -pe '…' **/*(.)

यदि आपका खोल bash ,4 है, तो आप चला सकते हैं perl … **/*, लेकिन यह बहुत ही त्रुटिपूर्ण त्रुटि संदेश उत्पन्न करेगा क्योंकि sed निर्देशिकाओं पर चलने की कोशिश करेगा (और विफल)। यदि आप केवल फ़ाइलों के एक सेट में प्रतिस्थापन करना चाहते हैं जैसे कि सी फ़ाइलें, तो आप मैचों को प्रतिबंधित कर सकते हैं (जो या तो बैश ≥4 या zsh में काम करता है):

perl -i -l -pe '…' **/*.[hc]

यदि आपको बेहतर नियंत्रण की आवश्यकता है कि आप किन फ़ाइलों की जगह ले रहे हैं, या आपके शेल में पुनरावर्ती निर्देशिका मिलान निर्माण नहीं है **, या यदि आपके पास बहुत अधिक फाइलें हैं और "कमांड लाइन बहुत लंबी" त्रुटि मिलती है, तो उपयोग करें find। उदाहरण के लिए, नामांकित *.hया *.cवर्तमान निर्देशिका में सभी फ़ाइलों और इसके उपनिर्देशिकाओं पर प्रतिस्थापन करने के लिए (पुराने सिस्टम पर, आपको पंक्ति के अंत में उपयोग \;करने की आवश्यकता हो सकती है +( +फ़ॉर्म तेज़ है लेकिन हर जगह उपलब्ध नहीं है)।

find . -type f -name '*.[hc]' -exec perl -i -l -pe '…' {} +

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

Emacs में, यहां बताया गया है कि आप यह कैसे कर सकते हैं:

  1. फ़ाइल नामों के साथ इकट्ठा करें M-x find-name-dired(एक toplevel निर्देशिका M-x find-diredनिर्दिष्ट करें ) या (एक मनमाना findकमांड लाइन निर्दिष्ट करें )।
  2. परिणामी बिंदीदार बफ़र में, tसभी फ़ाइलों को चिह्नित करने के लिए दबाएँ , फिर Q( dired-do-query-replace-regexp) चिह्नित फ़ाइलों पर संकेत देने के साथ प्रतिस्थापन करने के लिए।

1

sdiff(देखें http://www.gnu.org/software/diffutils/manual/diffutils.html#Invoking-sdiff ) यहाँ काम आ सकता है। इसके साथ आप इंटरैक्टिव पैचिंग कर सकते हैं। तो यह एक अस्थायी फ़ाइल के साथ कर आप प्रतिस्थापन कार्रवाई का उपयोग कर बनाया sedएक संभव समाधान हो सकता है:

# use file descriptor 3 to still allow use of stdin
while IFS= read -r -d '' file <&3; do

  # write the result of the replacement into a temporary file
  sed -r 's/something/something_else/g' -- "$file" > replacer_tmp

  if cmp -s -- "$file" replacer_tmp; then
    continue; # nothing was replaced
  fi

  echo "There is something to replace in '$file'! Starting interactive diff."
  echo

  sdiff -o "$file" -s -d -- "$file" replacer_tmp

  echo

done 3< <(find . -type f -print0)

(गैर-पोसिक्स प्रक्रिया प्रतिस्थापन के read -dरूप में फ़ाइल पाश का उपयोग करें और इसके द्वारा समर्थित है bash।)

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