कई फाइलें बदलें


194

निम्न आदेश 2 फ़ाइलों की सामग्री को सही ढंग से बदल रहा है।

sed -i 's/abc/xyz/g' xaa1 xab1 

लेकिन मुझे जो करने की ज़रूरत है वह कई ऐसी फ़ाइलों को गतिशील रूप से बदलने के लिए है और मुझे फ़ाइल नाम नहीं पता है। मैं एक कमांड लिखना चाहता हूं जो वर्तमान निर्देशिका से सभी फाइलों को पढ़ना शुरू करेगी xa*और sedफ़ाइल सामग्री को बदलना चाहिए।


62
तुम्हारा मतलब है sed -i 's/abc/xyz/g' xa*?
पॉल आर ०

3
यहाँ उत्तर पर्याप्त नहीं हैं। देखें unix.stackexchange.com/questions/112023/…
आइजैक

जवाबों:


136

और भी बेहतर:

for i in xa*; do
    sed -i 's/asd/dfg/g' $i
done

क्योंकि कोई नहीं जानता कि कितनी फाइलें हैं, और कमांड लाइन की सीमाएं तोड़ना आसान है।

जब बहुत सारी फाइलें होती हैं तो यहां क्या होता है:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#

18
यदि ऐसी कई फाइलें हैं, तो आप कमांड में कमांड लाइन की सीमा को तोड़ देंगे for। अपने आप को find ... | xargs ...
उससे

1
मैं कार्यान्वयन नहीं जानता, लेकिन "xa *" पैटर्न को किसी बिंदु पर विस्तारित करना पड़ता है। क्या शेल विस्तार के लिए अलग-अलग तरीके forसे करता है echoया इसके लिए करता है grep?
ग्लेन जैकमैन

4
अद्यतन उत्तर देखें। यदि आपको अधिक जानकारी की आवश्यकता है, तो कृपया एक आधिकारिक प्रश्न पूछें, ताकि लोग आपकी मदद कर सकें।
lenik

5
Sed कमांड में, आपको रिक्त स्थान के साथ फ़ाइलनाम पर शब्द विभाजन से बचने के "$i"बजाय उपयोग करने की आवश्यकता है $i। अन्यथा यह बहुत अच्छा है।
वाइल्डकार्ड

4
सूची के बारे में, मेरा मानना ​​है कि अंतर यह है कि forभाषा सिंटैक्स का हिस्सा है, न कि केवल एक अंतर्निहित। सभी sed -i 's/old/new' *के विस्तार के लिए , *सभी को arglist से sed के रूप में पारित किया जाना चाहिए, और मुझे पूरा यकीन है कि sedप्रक्रिया शुरू होने से पहले ही ऐसा होना चाहिए। forलूप का उपयोग करते हुए , पूर्ण arglist (का विस्तार *) कभी भी कमांड के रूप में पारित नहीं होता है, केवल शेल मेमोरी में संग्रहीत होता है और इसके माध्यम से पुनरावृत्त होता है। मेरे पास इसके लिए कोई संदर्भ नहीं है, हालांकि, यह सिर्फ संभावना है कि अंतर है। (मैं किसी और जानकार से सुनना पसंद करूंगा ...)
वाइल्डकार्ड

166

मुझे आश्चर्य है कि किसी ने भी -exec तर्क का उल्लेख नहीं किया है, जो इस प्रकार के उपयोग-मामले के लिए अभिप्रेत है, हालांकि यह प्रत्येक मिलान फ़ाइल नाम के लिए एक प्रक्रिया शुरू करेगा:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} \;

वैकल्पिक रूप से, कोई xargs का उपयोग कर सकता है, जो कम प्रक्रियाओं को लागू करेगा:

find . -type f -name 'xa*' | xargs sed -i 's/asd/dsg/g'

या अधिक बस सबप्रोसेस कॉल के लिए एक से अधिक फ़ाइल प्रदान करने के लिए खोजने के बजाय + निष्पादन संस्करण का उपयोग ;करें:

find . -type f -name 'xa*' -exec sed -i 's/asd/dsg/g' {} +

7
मुझे इस उत्तर में कमांड को इस तरह से संशोधित करना था: find ./ -type f -name 'xa*' -exec sed -i '' 's/asd/dsg/g' {} \;वह स्थान है जो कमांड कमांड के लिए है ./और -iOSX के बाद सिंगल कोट्स की एक जोड़ी है ।
शेल्बीडज़

खोज आदेश काम करता है क्योंकि यह ealfonso द्वारा आपूर्ति की जाती है, ./के बराबर है .और उसके बाद -iकेवल बैकअप बैकअप पैरामीटर है।

-execके साथ खोज का विकल्प {} +के रूप में कहा गया है समस्या को हल करने के लिए पर्याप्त है, और सबसे आवश्यकताओं के लिए ठीक होना चाहिए। लेकिन xargsसामान्य रूप से एक बेहतर विकल्प है क्योंकि यह -pविकल्प के साथ समानांतर प्रसंस्करण की भी अनुमति देता है। जब आपका ग्लोब विस्तार आपकी कमांड लाइन की लंबाई को ओवरफ्लो करने के लिए पर्याप्त होता है, तो आपको क्रमिक रन से अधिक स्पीडअप से भी लाभ होने की संभावना है।
अमित नायडू

78

आप एक साथ grep और sed का उपयोग कर सकते हैं। यह आपको उपनिर्देशिका को पुनरावर्ती खोज करने की अनुमति देता है।

Linux: grep -r -l <old> * | xargs sed -i 's/<old>/<new>/g'
OS X: grep -r -l <old> * | xargs sed -i '' 's/<old>/<new>/g'

For grep:
    -r recursively searches subdirectories 
    -l prints file names that contain matches
For sed:
    -i extension (Note: An argument needs to be provided on OS X)

3
मेरे लिए इस विधि का बोनस मैं एक grep -vफ़ोल्डर फ़ोल्डर से बचने के लिए पर्ची कर सकता थाgrep -rl <old> . | grep -v \.git | xargs sed -i 's/<old>/<new>/g'
मार्टिन लिन

एक मैक के लिए सबसे अच्छा समाधान!
मार्क्विस ब्लाउंट

30

sedमैक ओएस एक्स के साथ आने वाले डिफ़ॉल्ट में वे कमांड काम नहीं करेंगे।

से man 1 sed:

-i extension
             Edit files in-place, saving backups with the specified
             extension.  If a zero-length extension is given, no backup 
             will be saved.  It is not recommended to give a zero-length
             extension when in-place editing files, as you risk corruption
             or partial content in situations where disk space is exhausted, etc.

कोशिश की

sed -i '.bak' 's/old/new/g' logfile*

तथा

for i in logfile*; do sed -i '.bak' 's/old/new/g' $i; done

दोनों ठीक काम करते हैं।


2
@sumek यहाँ OS X पर एक उदाहरण टर्मिनल सत्र है जो सभी घटनाओं को बदलने के लिए sed दिखाता है: GitHub
Gist

मैंने इसका उपयोग अपनी सभी वेबसाइट की दो अलग-अलग लाइनों को एक-लाइनर से नीचे की फाइलों में बदलने के लिए किया। sed -i.bak "s / supercache_proxy_config /xy_includes \ / supercache_config / g; s / basic_proxy_config /xy_include \ / basic_proxy_config / g" साइट्स-उपलब्ध / * आप फ़ाइलों को हटाने के लिए मत भूलना * .bak फ़ाइलें। प्रणाली स्वच्छता की खातिर।
योशिय्याह

19

@PaRR ने इसे एक टिप्पणी के रूप में पोस्ट किया है, लेकिन लोगों को इसे एक उत्तर के रूप में देखना चाहिए (और यह उत्तर मेरी आवश्यकताओं के लिए सबसे अच्छा काम करता है):

sed -i 's/abc/xyz/g' xa*

यह मध्यम मात्रा में फ़ाइलों के लिए काम करेगा, संभवतः दसियों के आदेश पर, लेकिन शायद लाखों लोगों के आदेश पर नहीं


मान लें कि आपके प्रतिस्थापन में आगे-स्लैश हैं। फ़ाइलपथ के साथ एक और उदाहरण sed -i 's|auth-user-pass nordvpn.txt|auth-user-pass /etc/openvpn/nordvpn.txt|g' *.ovpn
लेओ लेपोल्ड हर्ट्ज़ '

10

एक और अधिक बहुमुखी तरीका उपयोग करने के लिए है find:

sed -i 's/asd/dsg/g' $(find . -type f -name 'xa*')

1
उस आदेश का आउटपुट विस्तारित हो जाता है, इसलिए यह समस्या का समाधान नहीं करता है। इसके बजाय आपको -exec का उपयोग करना चाहिए
ealfonso

@erjoalgo यह काम करता है क्योंकि sed कमांड कई इनपुट फ़ाइलों को संभाल सकता है। खोज कमांड का विस्तार वास्तव में इसे काम करने के लिए आवश्यक है।
dkinzer

यह तब तक काम करता है जब तक फ़ाइलों की संख्या कमांड लाइन की सीमाओं में नहीं धकेल देती है।
इलफोंसो

यह सीमा केवल मशीन के लिए उपलब्ध मेमोरी संसाधनों पर निर्भर है और यह निष्पादन के लिए सीमा के समान है।
डंकेर

4
यह बिल्कुल भी सच नहीं है। ऊपर आपके कमांड में, $ (खोजो ...) एक एकल कमांड में विस्तारित हो जाता है, जो बहुत लंबे समय तक हो सकता है यदि कई मिलान फ़ाइलें हैं। यदि यह बहुत लंबा है (उदाहरण के लिए मेरे सिस्टम में सीमा 2097152 वर्णों के आसपास है) तो आपको एक त्रुटि मिल सकती है: "तर्क सूची बहुत लंबी है" और कमांड विफल हो जाएगी। इस पर कुछ पृष्ठभूमि पाने के लिए कृपया इस त्रुटि को गूगल करें।
इलेफोंसो

2

मैं findसमान कार्य के लिए उपयोग कर रहा हूं । यह काफी सरल है: आपको इसे sedइस तरह से एक तर्क के रूप में पारित करना होगा:

sed -i 's/EXPRESSION/REPLACEMENT/g' `find -name "FILE.REGEX"`

इस तरह से आपको जटिल लूप लिखने की ज़रूरत नहीं है, और यह देखना सरल है कि आप किन फ़ाइलों को बदलने जा रहे हैं, बस चलाने findसे पहले sed



0

आप बना सकते हैं

' xxxx ' टेक्स्ट यू सर्च और इसे ' yyyy ' से बदल देगा

grep -Rn '**xxxx**' /path | awk -F: '{print $1}' | xargs sed -i 's/**xxxx**/**yyyy**/'

0

यदि आप एक स्क्रिप्ट चलाने में सक्षम हैं, तो मैंने ऐसी ही स्थिति के लिए क्या किया है:

sedकमांड के लिए एक शब्दकोश / हैशपॉप (सहयोगी सरणी) और चर का उपयोग करके , हम कई तारों को बदलने के लिए सरणी के माध्यम से लूप कर सकते हैं। एक वाइल्डकार्ड को name_patternवसीयत name_pattern='File*.txt'में शामिल करना एक विशिष्ट निर्देशिका में पैटर्न के साथ फाइलों में इन-प्लेस को बदलने की अनुमति देगा (यह कुछ ऐसा हो सकता है source_dir)। में सभी परिवर्तन लिखे logfileगए हैंdestin_dir

#!/bin/bash
source_dir=source_path
destin_dir=destin_path
logfile='sedOutput.txt'
name_pattern='File.txt'

echo "--Begin $(date)--" | tee -a $destin_dir/$logfile
echo "Source_DIR=$source_dir destin_DIR=$destin_dir "

declare -A pairs=( 
    ['WHAT1']='FOR1'
    ['OTHER_string_to replace']='string replaced'
)

for i in "${!pairs[@]}"; do
    j=${pairs[$i]}
    echo "[$i]=$j"
    replace_what=$i
    replace_for=$j
    echo " "
    echo "Replace: $replace_what for: $replace_for"
    find $source_dir -name $name_pattern | xargs sed -i "s/$replace_what/$replace_for/g" 
    find $source_dir -name $name_pattern | xargs -I{} grep -n "$replace_for" {} /dev/null | tee -a $destin_dir/$logfile
done

echo " "
echo "----End $(date)---" | tee -a $destin_dir/$logfile

सबसे पहले, जोड़े की सरणी घोषित की जाती है, प्रत्येक जोड़ी एक प्रतिस्थापन स्ट्रिंग है, फिर उसके WHAT1लिए प्रतिस्थापित किया जाएगा FOR1और फ़ाइल में OTHER_string_to replaceप्रतिस्थापित किया जाएगा । लूप में सरणी को पढ़ा जाता है, जोड़ी के पहले सदस्य के रूप में और दूसरे के रूप में पुनः प्राप्त किया जाता है । आदेश खोजें निर्देशिका में फ़ाइल नाम (है कि एक वाइल्डकार्ड हो सकती है) और एक ही फाइल (रों) क्या पहले से परिभाषित किया गया था में आदेश बदल देता है। अंत में मैंने फ़ाइल (ओं) में किए गए परिवर्तनों को लॉग करने के लिए लॉगफ़ाइल में रीडायरेक्ट किया।string replacedFile.txtreplace_what=$ireplace_for=$jfindsed -igrep

इसने मेरे लिए काम किया GNU Bash 4.3 sed 4.2.2और बैश में ट्यूप से अधिक लूप के लिए वास्यानोविकोव के जवाब पर आधारित ।

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