बैश या ज़शेल जैसे शेल का उपयोग करके, मैं एक पुनरावर्ती 'खोजने और बदलने' को कैसे कर सकता हूं? दूसरे शब्दों में, मैं इस निर्देशिका और इसकी उपनिर्देशिकाओं की सभी फाइलों में 'बार' की प्रत्येक घटना को 'बार' से बदलना चाहता हूं।
बैश या ज़शेल जैसे शेल का उपयोग करके, मैं एक पुनरावर्ती 'खोजने और बदलने' को कैसे कर सकता हूं? दूसरे शब्दों में, मैं इस निर्देशिका और इसकी उपनिर्देशिकाओं की सभी फाइलों में 'बार' की प्रत्येक घटना को 'बार' से बदलना चाहता हूं।
जवाबों:
यह कमांड इसे करेगा (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'
यहां देखिए यह कैसे काम करता है:
find . -type f -name '*.txt'
वर्तमान निर्देशिका में ( .
) और नीचे, सभी नियमित फ़ाइलें ( -type f
), जिनके नाम अंत में मिलते हैं.txt
|
अगले कमांड पर उस कमांड (फाइलनाम की एक सूची) का आउटपुट पास करता हैxargs
उन फ़ाइलनामों को इकट्ठा करता है और उन्हें एक-एक करके हाथ करता है sed
sed -i '' -e 's/foo/bar/g'
इसका मतलब है "फ़ाइल को एक बैकअप के बिना, जगह में संपादित करें, और निम्न प्रतिस्थापन ( s/foo/bar
प्रति पंक्ति कई बार /g
) ( )" देखें (देखें man sed
)ध्यान दें कि लाइन 4 में 'बैकअप के बिना' हिस्सा मेरे लिए ठीक है, क्योंकि मैं जिन फाइलों को बदल रहा हूं, वे वैसे भी संस्करण नियंत्रण में हैं, इसलिए अगर कोई गलती हुई तो मैं आसानी से पूर्ववत कर सकता हूं।
-print0
विकल्प के बिना xargs करने के लिए उत्पादन मिल । आपका कमांड उनके नाम पर रिक्त स्थान आदि की फाइलों पर विफल हो जाएगा।
find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +
यह सब GNU खोजने के साथ करेंगे।
sed: can't read : No such file or directory
जब मैं दौड़ता हूं तो मुझे मिलता है find . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'
, लेकिन find . -name '*.md' -print0
कई फाइलों की सूची देता है।
-i
और''
''
इसका sed -i
क्या अर्थ है ''
?
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
यह xargs
निर्भरता को दूर करता है।
sed
, इसलिए अधिकांश प्रणालियों पर विफल हो जाएगा। जीएनयू sed
के बीच कोई अंतराल डाल करने की आवश्यकता है -i
और ''
।
यदि आप Git का उपयोग कर रहे हैं तो आप यह कर सकते हैं:
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
केवल फ़ाइल नाम सूचीबद्ध करता है। -z
प्रत्येक परिणाम के बाद एक शून्य बाइट प्रिंट करता है।
मैंने यह करना समाप्त कर दिया क्योंकि किसी प्रोजेक्ट की कुछ फाइलों में फाइल के अंत में एक नई लाइन नहीं थी, और जब कोई अन्य परिवर्तन नहीं हुआ तब भी sed ने एक नई पंक्ति जोड़ दी। (कोई टिप्पणी नहीं कि फाइलों के अंत में एक नई पंक्ति होनी चाहिए। or)
find ... -print0 | xargs -0 sed ...
समाधान न केवल बहुत अधिक समय लेते हैं, बल्कि उन फाइलों में भी नई-नई धाराएँ जोड़ते हैं, जिनमें पहले से कोई नहीं होता है, जो एक git रेपो के अंदर काम करते समय एक उपद्रव है। git grep
तुलना द्वारा तेजी से प्रकाश है।
यहाँ मेरा 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
(उदाहरण के लिए)
प्रयत्न, कोशिश:
sed -i 's/foo/bar/g' $(find . -type f)
उबंटू 12.04 पर परीक्षण किया गया।
यदि उपनिर्देशिका के नाम और / या फ़ाइल नाम में रिक्त स्थान हैं तो यह कमांड काम नहीं करेगा, लेकिन यदि आपके पास है तो वे इस कमांड का उपयोग नहीं करेंगे क्योंकि यह काम नहीं करेगा।
यह आमतौर पर निर्देशिका नामों और फ़ाइल नाम में रिक्त स्थान का उपयोग करने के लिए एक बुरा अभ्यास है।
http://linuxcommand.org/lc3_lts0020.php
"फ़ाइल नामों के बारे में महत्वपूर्ण तथ्य" देखें
मैं अब इस शेल स्क्रिप्ट का उपयोग करता हूं, जो उन चीजों को जोड़ती है जो मैंने दूसरे उत्तरों से सीखीं और वेब पर खोज कीं। मैंने इसे 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
मेरा उपयोग मामला था मैं उसके 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'
उम्मीद है कि मदद की।
निम्न आदेश ने 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
।
# 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'