कमांड लाइन: grep द्वारा मिलान किए गए सभी फ़ाइलनामों में खोजें और बदलें


80

मैं grep द्वारा मेल की गई सभी फ़ाइलों में एक स्ट्रिंग को खोजने और बदलने की कोशिश कर रहा हूं:

grep -n 'foo' * मुझे फॉर्म में आउटपुट देगा:

[filename]:[line number]:[text]

ग्रेप द्वारा लौटाए गए प्रत्येक फ़ाइल के लिए, मैं बदल कर फ़ाइल को संशोधित करना चाहते हैं fooके साथ bar

जवाबों:


70

क्या आपका मतलब है grep द्वारा मेल की गई सभी फाइलों में एक स्ट्रिंग को खोजना और बदलना?

perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

संपादित करें

चूंकि यह एक काफी लोकप्रिय प्रश्न लगता है कि मैं अपडेट करूंगा।

आजकल मैं ज्यादातर इस्तेमाल ack-grepकरता हूं क्योंकि यह अधिक उपयोगकर्ता के अनुकूल है। तो उपरोक्त आदेश होगा:

perl -p -i -e 's/old/new/g' `ack -l searchpattern`

फ़ाइल नाम में व्हॉट्सएप को संभालने के लिए आप चला सकते हैं:

ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

आप अधिक कर सकते हैं ack-grep। आप केवल HTML फ़ाइलों में खोज को प्रतिबंधित करना चाहते हैं:

ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

और अगर सफेद जगह कोई समस्या नहीं है तो यह और भी कम है:

perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files

3
मुझे लगता है कि इसमें उन फ़ाइलों / फ़ोल्डरों के साथ समस्या हो सकती है जिनमें रिक्त स्थान हैं। Can't open Untitled: No such file or directory, <> line 5"अनटाइटल्ड फोल्डर / file.txt" को आज़माते समय।
20

108

ऐसा प्रतीत होता है कि आप क्या चाहते हैं, आपके द्वारा दिए गए उदाहरण के आधार पर:

sed -i 's/foo/bar/g' *

यह पुनरावर्ती नहीं है (यह उपनिर्देशिकाओं में नहीं उतरेगा)। एक अच्छा समाधान के लिए एक पेड़ भर में चयनित फ़ाइलों की जगह मैं पाऊँगा:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

*.htmlअभिव्यक्ति है कि फ़ाइलों से मेल खाना चाहिए है, .bakके बाद -iमूल फ़ाइल की एक प्रतिलिपि, एक .bak विस्तार के साथ बनाता है (यह किसी भी विस्तार आप की तरह हो सकता है) और gsed अभिव्यक्ति के अंत में पर कई प्रतियां को बदलने के लिए sed बताता है एक पंक्ति (केवल पहले वाले के बजाय)। -printखोजने के लिए शो के लिए एक सुविधा है जो फ़ाइलों का मिलान नहीं हुआ जा रहा था है। यह सब आपके सिस्टम पर इन उपकरणों के सटीक संस्करणों पर निर्भर करता है।


1
साइबर उपयोगकर्ताओं के लिए चेतावनी का एक शब्द। लगता है और sed कॉम्बो के माध्यम से स्ट्रीम की गई फ़ाइलों के लिए उपयोगकर्ता अधिकारों को बदलने लगता है। यह केवल कमांड चोद -R 644 * को उसी dir लेवल से इस्तेमाल करके तय किया जा सकता है जो कि जब / खोजे / संचालित होता था तब उपयोग किया जाता था।
kaskelotti

लोगों के लिए चेतावनी का एक शब्द जो -i तर्क का उपयोग नहीं करना चाहता: यदि आप इसका उपयोग नहीं करते हैं, तो यह काम नहीं करता है (मुझसे क्यों न पूछें)
knocte

4
@knocte -i फ़ाइल को संशोधित करने के लिए sed को बताता है, अन्यथा यह केवल संशोधित संस्करण को stdout में प्रिंट करता है। यदि आप .bak फ़ाइल नहीं चाहते हैं, तो '.bak' भाग को छोड़ दें, -i स्टैंडअलोन भी काम करता है।
मैटजे

2
OSX पर आपको findकमांड को उदाहरण के लिए find . -name '*.html'या से शुरू करने के लिए एक निर्देशिका देने की आवश्यकता है find directoryname/ -name '*'
मिकिएल कौव-ए-तजो

मुझे एक-ई जोड़ने की जरूरत है, अन्यथा यह 's ...' भाग का प्रत्यय लगता थाsed -ie 's/foo/bar/g' *
vish

14

यदि आपके sed(1)पास एक -iविकल्प है, तो इसे इस तरह उपयोग करें:

for i in *; do
  sed -i 's/foo/bar/' $i
done

यदि नहीं, तो आप जिस भाषा के साथ खेलना चाहते हैं, उसके आधार पर कई तरीके भिन्न होते हैं:

ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
perl -pi.bak -e 's/foo/bar/' *

2
for i in *; do ...बेमानी है, sed तर्क के रूप में फाइलों की एक सूची ले सकता है।
जेन्स

2
@ जेन्स ऊपर दिए गए अपने स्वयं के उदाहरण को जोड़कर इस उत्तर में सुधार क्यों नहीं करते?
मैगपाई

चर हमेशा उद्धृत किया जाना चाहिएfor i in *; do; sed -i 's/foo/bar/g' "$i"; done
बिल्ली

6

मुझे उपरोक्त समाधान या सिस्टम वाइड खोज और हजारों फ़ाइलों के बीच की जगह पसंद है और उनका उपयोग किया है:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

मैं '* .htm' के साथ ग्रहण करता हूं। .html के बजाय यह खोज करता है और ढूँढता है।

मैं .bak को अधिक सिस्टम वाइड यूज्ड टिल्ड (~) से बदल देता हूं ताकि बैकअप फाइलों को आसानी से साफ किया जा सके।


4

यह पर्ल का उपयोग करने या खोजने की आवश्यकता के बिना grep का उपयोग करके काम करता है।

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

xargs का OSX या BSD openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/ पर एक -i नहीं है। क्या आपका मतलब ऊपरी मामले "I" का उपयोग करना था?
टोनी एडम्स

मुझे नहीं पता -iथा कि अन्य ओएस के लिए काम नहीं किया। Ubuntu पर मेरे लिए काम करता है।
16

मेरा xargsमैनपेज (उबंटू पर) कहता है कि -iयह पदावनत है, और -Iइसके बजाय उपयोग करना है। तो, हमें कहना चाहिए:grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath
मैक्स वालेस

3

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd>प्रति बैच एक दुभाषिया लोड करते समय कई स्थान समाहित करता है। काफी तेज।


@knocte, "cmd" संपूर्ण खोज के लिए एक टोकन है और दिए गए टूल को जो भी चुनता है उसके लिए कमांड को प्रतिस्थापित करता है। यह जवाब इस सवाल का जवाब देता है कि श्वेत-स्थान वाले फ़ाइल नामों से कैसे निपटें।
टोनी एडम्स

1

जवाब पहले से ही खोजने और sed का उपयोग कर दिया

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

शायद मानक जवाब है। या आप कमांड के perl -pi -e s/foo/bar/g'बजाय उपयोग कर सकते हैं sed

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

rpl -R foo bar .

यह अधिकांश लिनक्स डिस्ट्रो पर डिफ़ॉल्ट रूप से उपलब्ध नहीं है लेकिन इंस्टॉल ( apt-get install rplया समान) करने के लिए त्वरित है ।

हालाँकि, कठिन नौकरियों के लिए जिनमें नियमित अभिव्यक्तियाँ और बैक प्रतिस्थापन शामिल हैं, या नाम बदलने के साथ-साथ खोज-और-फ़ाइल भी दर्ज करते हैं, सबसे सामान्य और शक्तिशाली उपकरण जिसके बारे में मुझे पता है, वह रीप्रैन है , एक छोटा पायथन स्क्रिप्ट जिसे मैंने कुछ समय पहले लिखा था कांटेदार नामकरण और कार्यों को फिर से शुरू करना। आप इसे पसंद कर सकते हैं कारण हैं:

  • फ़ाइलों का नाम बदलने के साथ-साथ फ़ाइल सामग्रियों पर खोज और प्रतिस्थापन का समर्थन करें (निर्देशिकाओं के बीच फ़ाइलों को ले जाने और नई मूल निर्देशिका बनाने सहित)।
  • खोज करने और बदलने के लिए प्रतिबद्ध होने से पहले परिवर्तन देखें।
  • बैक प्रतिस्थापन, पूरे शब्द, केस असंवेदनशील और केस प्रोटेक्टिंग (foo -> बार, फू -> बार, FOO -> BAR) मोड के साथ नियमित अभिव्यक्ति का समर्थन करें।
  • कई प्रतिस्थापनों के साथ काम करता है, जिसमें स्वैप (foo -> बार और बार -> foo) या गैर-विशिष्ट प्रतिस्थापन के सेट (foo -> बार, f -> x) शामिल हैं।

उदाहरणों के लिए README की जाँच करें


1

यह वास्तव में ऐसा लगता है की तुलना में आसान है।

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep आपके पेड़ (-R) के माध्यम से पुनरावृत्ति करता है और वर्तमान निर्देशिका (./) पर शुरू होने वाले फ़ाइल नाम (-l) को प्रिंट करता है।
  • कि xargs को पाइप किया जाता है, जो उन्हें एक बार (-n 1) में संसाधित करता है, और शेल कमांड (sh -c) में प्लेसहोल्डर (-I%) के रूप में% का उपयोग करता है
  • शेल कमांड में, पहले फ़ाइल का नाम मुद्रित होता है (ls%;)
  • फिर sed एक इनलाइन ऑपरेशन (-i) करता है, फ़ाइल के बार (foo / बार) के साथ फू का एक विकल्प ('s /'), ग्लोबली (/ g) फ़ाइल पर (फिर से,% द्वारा दर्शाया गया)

बहुत आसान। अगर आपको grep, xargs, sed, और awk पर एक अच्छी समझ मिलती है, तो यह लगभग कुछ भी असंभव नहीं है जब यह bash में टेक्स्ट फाइल में हेरफेर की बात आती है :)


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