मैं कमांड लाइन से एक पुनरावर्ती खोज और प्रतिस्थापन कैसे कर सकता हूं?


84

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


उन्हीं सवालों के लिए एक वैकल्पिक जवाब यहां पाया जा सकता है stackoverflow.com/questions/9704020/…
dunxd


यह एक अच्छा विचार हो सकता है इस कोशिश में। इस तरह से आप पुष्टिकरण सुविधा का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि आप उस चीज़ को स्वैप न करें जिसका आप इरादा नहीं करते हैं। मुझे यकीन नहीं है कि यह निर्देशिका विस्तृत किया जा सकता है।
सैमी बेनचेरी

जवाबों:


104

यह कमांड इसे करेगा (Mac OS X Lion और Kubuntu Linux दोनों पर परीक्षण किया गया है)।

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

यहां देखिए यह कैसे काम करता है:

  1. find . -type f -name '*.txt'वर्तमान निर्देशिका में ( .) और नीचे, सभी नियमित फ़ाइलें ( -type f), जिनके नाम अंत में मिलते हैं.txt
  2. | अगले कमांड पर उस कमांड (फाइलनाम की एक सूची) का आउटपुट पास करता है
  3. xargs उन फ़ाइलनामों को इकट्ठा करता है और उन्हें एक-एक करके हाथ करता है sed
  4. sed -i '' -e 's/foo/bar/g'इसका मतलब है "फ़ाइल को एक बैकअप के बिना, जगह में संपादित करें, और निम्न प्रतिस्थापन ( s/foo/barप्रति पंक्ति कई बार /g) ( )" देखें (देखें man sed)

ध्यान दें कि लाइन 4 में 'बैकअप के बिना' हिस्सा मेरे लिए ठीक है, क्योंकि मैं जिन फाइलों को बदल रहा हूं, वे वैसे भी संस्करण नियंत्रण में हैं, इसलिए अगर कोई गलती हुई तो मैं आसानी से पूर्ववत कर सकता हूं।


9
कभी भी पाइप -print0विकल्प के बिना xargs करने के लिए उत्पादन मिल । आपका कमांड उनके नाम पर रिक्त स्थान आदि की फाइलों पर विफल हो जाएगा।
slhck

20
इसके अलावा, find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +यह सब GNU खोजने के साथ करेंगे।
डैनियल एंडरसन

6
sed: can't read : No such file or directoryजब मैं दौड़ता हूं तो मुझे मिलता है find . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g', लेकिन find . -name '*.md' -print0कई फाइलों की सूची देता है।
मार्टिन थोमा

9
यह मेरे लिए काम करता है अगर मैं के बीच की जगह को दूर -iऔर''
कनाडा ल्यूक

3
आखिर भूमिका क्या है, ''इसका sed -iक्या अर्थ है ''?
जस

27
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

यह xargsनिर्भरता को दूर करता है।


4
यह जीएनयू के साथ काम नहीं करता है sed, इसलिए अधिकांश प्रणालियों पर विफल हो जाएगा। जीएनयू sedके बीच कोई अंतराल डाल करने की आवश्यकता है -iऔर ''
slhck

1
स्वीकृत उत्तर इसे समझाने का बेहतर काम करते हैं। लेकिन सही सिंटैक्स का उपयोग करने के लिए +1।
orodbhen

9

यदि आप Git का उपयोग कर रहे हैं तो आप यह कर सकते हैं:

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-lकेवल फ़ाइल नाम सूचीबद्ध करता है। -zप्रत्येक परिणाम के बाद एक शून्य बाइट प्रिंट करता है।

मैंने यह करना समाप्त कर दिया क्योंकि किसी प्रोजेक्ट की कुछ फाइलों में फाइल के अंत में एक नई लाइन नहीं थी, और जब कोई अन्य परिवर्तन नहीं हुआ तब भी sed ने एक नई पंक्ति जोड़ दी। (कोई टिप्पणी नहीं कि फाइलों के अंत में एक नई पंक्ति होनी चाहिए। or)


1
इस समाधान के लिए बिग +1। बाकी find ... -print0 | xargs -0 sed ...समाधान न केवल बहुत अधिक समय लेते हैं, बल्कि उन फाइलों में भी नई-नई धाराएँ जोड़ते हैं, जिनमें पहले से कोई नहीं होता है, जो एक git रेपो के अंदर काम करते समय एक उपद्रव है। git grepतुलना द्वारा तेजी से प्रकाश है।
जोश कुपर्शमीद

3

यहाँ मेरा zsh / perl फंक्शन है जिसका मैं इसके लिए उपयोग करता हूँ:

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

और मैं इसका उपयोग करके निष्पादित करूंगा

$ change foo bar **/*.java

(उदाहरण के लिए)


2

प्रयत्न, कोशिश:

sed -i 's/foo/bar/g' $(find . -type f)

उबंटू 12.04 पर परीक्षण किया गया।

यदि उपनिर्देशिका के नाम और / या फ़ाइल नाम में रिक्त स्थान हैं तो यह कमांड काम नहीं करेगा, लेकिन यदि आपके पास है तो वे इस कमांड का उपयोग नहीं करेंगे क्योंकि यह काम नहीं करेगा।

यह आमतौर पर निर्देशिका नामों और फ़ाइल नाम में रिक्त स्थान का उपयोग करने के लिए एक बुरा अभ्यास है।

http://linuxcommand.org/lc3_lts0020.php

"फ़ाइल नामों के बारे में महत्वपूर्ण तथ्य" देखें


जब आपके पास उनके नामों में स्थान (नों) के साथ फ़ाइल हो तो इसे आज़माएँ। (अंगूठे का एक नियम है जो कहता है, "अगर कोई चीज़ सच होने के लिए बहुत अच्छी लगती है, तो यह शायद है।" यदि आपने "खोज" की है तो ऐसा समाधान जो किसी भी चीज़ से अधिक कॉम्पैक्ट है, जो 3½ वर्षों में पोस्ट किया गया है, आपको खुद से पूछना चाहिए कि क्यों यह हो सकता है।)
स्कॉट

1

इस शैल स्क्रिप्ट का उपयोग करें

मैं अब इस शेल स्क्रिप्ट का उपयोग करता हूं, जो उन चीजों को जोड़ती है जो मैंने दूसरे उत्तरों से सीखीं और वेब पर खोज कीं। मैंने इसे changeअपने $PATHऔर फ़ोल्डर में एक फ़ाइल में रखा था chmod +x change

#!/bin/bash
function err_echo {
  >&2 echo "$1"
}

function usage {
  err_echo "usage:"
  err_echo '  change old new foo.txt'
  err_echo '  change old new foo.txt *.html'
  err_echo '  change old new **\*.txt'
  exit 1
}

[ $# -eq 0 ] && err_echo "No args given" && usage

old_val=$1
shift
new_val=$1
shift
files=$* # the rest of the arguments

[ -z "$old_val" ]  && err_echo "No old value given" && usage
[ -z "$new_val" ]  && err_echo "No new value given" && usage
[ -z "$files" ]    && err_echo "No filenames given" && usage

for file in $files; do
  sed -i '' -e "s/$old_val/$new_val/g" $file
done

0

मेरा उपयोग मामला था मैं उसके foo:/Drive_Letterसाथ प्रतिस्थापित करना चाहता था foo:/bar/baz/xyz मेरे मामले में मैं निम्नलिखित कोड के साथ ऐसा करने में सक्षम था। मैं उसी डायरेक्टरी लोकेशन में था, जहाँ ढेर सारी फाइलें थीं।

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

उम्मीद है कि मदद की।


0

निम्न आदेश ने Ubuntu और CentOS पर ठीक काम किया; हालाँकि, OS XI के अंतर्गत त्रुटियां आती रहीं:

find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;

sed: 1: "./Root": अमान्य कमांड कोड।

जब मैंने xargs के माध्यम से परमेस को पास करने की कोशिश की तो यह बिना किसी त्रुटि के ठीक काम किया:

find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'

आपके द्वारा परिवर्तित -iकिया गया तथ्य -i ''संभवतः आपके द्वारा बदले -execगए तथ्य से अधिक प्रासंगिक है -print0 | xargs -0। BTW, आप शायद की जरूरत नहीं है -e
स्कॉट

0
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

ऊपर एक आकर्षण की तरह काम किया है, लेकिन जुड़े निर्देशिकाओं के साथ, मुझे -Lइसमें झंडा जोड़ना होगा। अंतिम संस्करण इस तरह दिखता है:

# Recursively find and replace in files
find -L . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.