एक बैश कमांड से टेक्स्ट फ़ाइल के अंदर खोजें और बदलें


561

एक खोज करने के लिए और किसी दिए गए इनपुट स्ट्रिंग के लिए की जगह, का कहना है कि सबसे आसान तरीका क्या है abc, और एक अन्य स्ट्रिंग के साथ बदलें, का कहना है कि XYZफाइल में /tmp/file.txt?

मैं एक ऐप लिख रहा हूं और SSH के माध्यम से कमांड निष्पादित करने के लिए IronPython का उपयोग कर रहा हूं - लेकिन मैं यूनिक्स को अच्छी तरह से नहीं जानता हूं और नहीं जानता कि क्या देखना है।

मैंने सुना है कि बैश, एक कमांड लाइन इंटरफ़ेस होने के अलावा, एक बहुत ही शक्तिशाली स्क्रिप्टिंग भाषा हो सकती है। तो, अगर यह सच है, तो मुझे लगता है कि आप इन जैसे कार्य कर सकते हैं।

क्या मैं इसे बैश कर सकता हूं, और अपने लक्ष्य को प्राप्त करने के लिए सबसे सरल (एक पंक्ति) स्क्रिप्ट क्या है?

जवाबों:


927

सबसे आसान तरीका सेड (या पर्ल) का उपयोग करना है:

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

जो -iविकल्प के कारण इन-प्लेस एडिट करने के लिए sed को इनवाइट करेगा । इसे बैश से बुलाया जा सकता है।

यदि आप वास्तव में सिर्फ बैश का उपयोग करना चाहते हैं, तो निम्नलिखित काम कर सकते हैं:

while read a; do
    echo ${a//abc/XYZ}
done < /tmp/file.txt > /tmp/file.txt.t
mv /tmp/file.txt{.t,}

यह प्रत्येक पंक्ति पर लूप करता है, एक प्रतिस्थापन करता है, और एक अस्थायी फ़ाइल पर लिखता है (इनपुट को क्लोब नहीं करना चाहता)। अंत में कदम मूल नाम के लिए अस्थायी चलता है।


3
सिवाय इसके कि एमवी का इस्तेमाल करना 'नॉन बैश' सेड का इस्तेमाल करने के समान है। मैंने लगभग एक ही गूंज के बारे में कहा, लेकिन यह एक शेल बिलिन है।
पतला

5
हालांकि, सी के लिए -i तर्क सोलारिस के लिए मौजूद नहीं है (और मुझे लगता है कि कुछ अन्य कार्यान्वयन भी होंगे), इसलिए इसे ध्यान में रखें। बस कई मिनट बिताए जो यह पता
लगाते हैं

2
स्वयं पर ध्यान दें: की नियमित अभिव्यक्ति के बारे में sed: s/..../..../ - Substituteऔर /g - Global
चेक

89
मैक उपयोगकर्ताओं के लिए ध्यान दें, जिन्हें invalid command code Cत्रुटि मिलती है ... इन-प्लेस प्रतिस्थापन के लिए, बीएसडी sedको -iध्वज के बाद एक फ़ाइल एक्सटेंशन की आवश्यकता होती है क्योंकि यह दिए गए एक्सटेंशन के साथ बैकअप फ़ाइल को बचाता है। उदाहरण के लिए: sed -i '.bak' 's/find/replace/' /file.txt आप खाली स्ट्रिंग का उपयोग करके बैकअप को छोड़ सकते हैं जैसे:sed -i '' 's/find/replace/' /file.txt
ऑस्टिन

8
टिप: यदि आप केस असंवेदनशील s/abc/XYZ/gi
रेप्लस

166

फ़ाइल हेरफेर आम तौर पर बैश द्वारा नहीं किया जाता है, लेकिन बैश द्वारा आमंत्रित कार्यक्रमों द्वारा, जैसे:

perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

-iझंडा यह बताता है एक में जगह प्रतिस्थापन करना है।

man perlrunअधिक जानकारी के लिए देखें, जिसमें मूल फ़ाइल का बैकअप लेना शामिल है।


37
मुझ में शुद्धतावादी का कहना है कि आप सुनिश्चित नहीं हो सकते कि पर्ल सिस्टम पर उपलब्ध होगा। लेकिन आजकल मामला बहुत कम है। शायद मैं अपनी उम्र दिखा रहा हूं।
पतला

3
क्या आप अधिक जटिल उदाहरण दिखा सकते हैं। "Chdir / blah2" के साथ "chdir / blah2" को बदलने जैसा कुछ। मैंने कोशिश की perl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text, लेकिन मुझे पैटर्न के बीच कोई जगह नहीं होने के साथ एक त्रुटि मिलती रहती है और निम्नलिखित शब्द -e लाइन पर पदावनत हो जाता है। 1. बेजोड़ (regex में; <- HERE in m / (chdir) द्वारा चिह्नित () (<- HERE ?: \\ / at -e लाइन 1.
CMCDragonkai

@CMCDragonkai इस उत्तर की जाँच करें: stackoverflow.com/a/12061491/2730528
Alfonso Santiago

69

मुझे आश्चर्य हुआ जब मैंने इस पर ठोकर खाई ...

एक replaceकमांड है जो "mysql-server"पैकेज के साथ जहाज करता है, इसलिए यदि आपने इसे स्थापित किया है तो इसे आज़माएं:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

इसके man replaceलिए और देखें ।


12
यहां दो चीजें संभव हैं: ए) replaceएक उपयोगी स्वतंत्र उपकरण है और MySQL के लोगों को इसे अलग से जारी करना चाहिए और इस पर निर्भर होना चाहिए b) replaceको MySQL o_O के कुछ बिट की आवश्यकता है। किसी भी तरह से mysql- सर्वर को स्थापित करने के लिए इसे स्थापित करना गलत काम होगा। :)
फिलिप व्हाइटहाउस

केवल मैक के लिए काम करता है? मेरे ubuntu I centos में वह कमांड मौजूद नहीं है
paul

1
ऐसा इसलिए है क्योंकि आपके पास mysql-serverपैकेज स्थापित नहीं है । जैसा कि @rayro द्वारा बताया गया है, replaceइसका एक हिस्सा है।
15

2
"चेतावनी: प्रतिस्थापित को हटा दिया गया है और भविष्य के संस्करण में हटा दिया जाएगा।"
स्टीवन वचोन

1
Windows पर REPLACE कमांड को न चलाने के लिए सावधान रहें! विंडोज पर REPLACE कमांड फाइलों के तेजी से प्रतिकृति के लिए है। इस चर्चा के लिए प्रासंगिक नहीं है।
Maor

52

यह एक पुरानी पोस्ट है लेकिन @centurian के रूप में वैरिएबल का उपयोग करने के इच्छुक किसी भी व्यक्ति के लिए सिंगल कोट्स का मतलब है कि कुछ भी विस्तारित नहीं होगा।

चर प्राप्त करने का एक सरल तरीका यह है कि स्ट्रिंग समवर्ती किया जाए क्योंकि यह बक्सा में जुक्सपोजिशन द्वारा किया जाता है, निम्नलिखित कार्य करने चाहिए:

sed -i -e "s/$var1/$var2/g" /tmp/file.txt

39

बैश, अन्य गोले की तरह, अन्य कमांड के समन्वय के लिए सिर्फ एक उपकरण है। आमतौर पर आप मानक UNIX आदेशों का उपयोग करने की कोशिश करेंगे, लेकिन आप निश्चित रूप से किसी भी चीज़ को आह्वान करने के लिए बैश का उपयोग कर सकते हैं, जिसमें आपके स्वयं के संकलित कार्यक्रम, अन्य शेल स्क्रिप्ट, पायथन और पर्ल स्क्रिप्ट आदि शामिल हैं।

इस मामले में, इसे करने के कुछ तरीके हैं।

यदि आप किसी फ़ाइल को पढ़ना चाहते हैं, और उसे किसी अन्य फ़ाइल पर लिखें, तो जैसे ही आप जाते हैं, खोज / प्रतिस्थापित करें, sed का उपयोग करें:

sed 's/abc/XYZ/g' <infile >outfile

यदि आप फ़ाइल को जगह में संपादित करना चाहते हैं (जैसे कि किसी संपादक में फ़ाइल खोलना, उसे संपादित करना, तो उसे सहेजना) लाइन संपादक को 'पूर्व' के निर्देशों की आपूर्ति करना

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex फुलस्क्रीन मोड के बिना vi जैसा है। आप इसे vi के ':' प्रॉम्प्ट पर वही कमांड दे सकते हैं।


33

मुझे यह धागा दूसरों के बीच मिला और मैं मानता हूं कि इसमें सबसे पूर्ण उत्तर शामिल हैं इसलिए मैं अपना भी जोड़ रहा हूं:

  1. sedऔर edबहुत उपयोगी हैं ... हाथ से। इस कोड को @ जॉनी से देखें:

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
  2. जब मेरा प्रतिबंध शेल स्क्रिप्ट में इसका उपयोग करने के लिए है, तो "एबीसी" या "एक्सज़ेड" के स्थान पर किसी भी चर का उपयोग अंदर नहीं किया जा सकता है। BashFAQ क्या मैं कम से कम समझते हैं के साथ सहमत करने लगता है। इसलिए, मैं उपयोग नहीं कर सकता:

    x='abc'
    y='XYZ'
    sed -i -e 's/$x/$y/g' /tmp/file.txt
    #or,
    sed -i -e "s/$x/$y/g" /tmp/file.txt

    लेकिन हम क्या कर सकते हैं? जैसा कि, @ जॉनी ने कहा कि while read...लेकिन, दुर्भाग्य से यह कहानी का अंत नहीं है। निम्नलिखित ने मेरे साथ अच्छा काम किया:

    #edit user's virtual domain
    result=
    #if nullglob is set then, unset it temporarily
    is_nullglob=$( shopt -s | egrep -i '*nullglob' )
    if [[ is_nullglob ]]; then
       shopt -u nullglob
    fi
    while IFS= read -r line; do
       line="${line//'<servername>'/$server}"
       line="${line//'<serveralias>'/$alias}"
       line="${line//'<user>'/$user}"
       line="${line//'<group>'/$group}"
       result="$result""$line"'\n'
    done < $tmp
    echo -e $result > $tmp
    #if nullglob was set then, re-enable it
    if [[ is_nullglob ]]; then
       shopt -s nullglob
    fi
    #move user's virtual domain to Apache 2 domain directory
    ......
  3. जैसा कि कोई देख सकता nullglobहै अगर सेट किया गया है, तो यह अजीब व्यवहार करता है जब इसमें एक स्ट्रिंग होती है *:

    <VirtualHost *:80>
     ServerName www.example.com

    जो बन जाता है

    <VirtualHost ServerName www.example.com

    कोई समाप्ति कोण ब्रैकेट नहीं है और Apache2 भी लोड नहीं कर सकता है।

  4. इस तरह के पार्सिंग को एक हिट खोज की तुलना में धीमा होना चाहिए और प्रतिस्थापित करें, लेकिन जैसा कि आपने पहले ही देखा था, एक पार्स चक्र से बाहर काम करने वाले चार अलग-अलग खोज पैटर्न के लिए चार चर हैं।

सबसे उपयुक्त समाधान मैं समस्या की दी गई मान्यताओं के साथ सोच सकता हूं।


12
आपके (2) में - आप कर सकते हैं sed -e "s/$x/$y/", और यह काम करेगा। दोहरे भाव नहीं। यह गंभीर रूप से भ्रमित कर सकता है यदि चर में तार स्वयं विशेष अर्थ वाले वर्ण होते हैं। उदाहरण के लिए यदि x = "/" या x = "\"। जब आप इन मुद्दों को मारते हैं, तो इसका मतलब है कि आपको इस नौकरी के लिए शेल का उपयोग करने से रोकना चाहिए।
स्लिम

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

20

आप उपयोग कर सकते हैं sed:

sed -i 's/abc/XYZ/gi' /tmp/file.txt

-i"अनदेखा मामला" के लिए उपयोग करें यदि आप सुनिश्चित नहीं हैं कि पाठ खोजना है abcया ABCया AbC, आदि।

आप उपयोग कर सकते हैं findऔर sedयदि आपको अपना फ़ाइल नाम नहीं पता है:

find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

सभी पायथन फ़ाइलों को ढूंढें और बदलें:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;

12

आप edइन-फ़ाइल खोज और प्रतिस्थापित करने के लिए कमांड का उपयोग भी कर सकते हैं :

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

" स्क्रिप्ट्स के माध्यम से फ़ाइलों का संपादनed " में अधिक देखें ।


3
यह समाधान GNU / FreeBSD (Mac OSX) असंगतताओं (इसके विपरीत sed -i <pattern> <filename>) से स्वतंत्र है । बहुत अच्छा!
पीटरिनो

11

यदि आप जिस फ़ाइल पर काम कर रहे हैं, वह इतनी बड़ी नहीं है, और अस्थायी रूप से इसे एक चर में संग्रहीत करना कोई समस्या नहीं है, तो आप एक ही बार में पूरी फ़ाइल पर बैश स्ट्रिंग प्रतिस्थापन का उपयोग कर सकते हैं - लाइन द्वारा इसे लाइन पर जाने की कोई आवश्यकता नहीं है:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

पूरी फ़ाइल सामग्री को लाइनब्रेक सहित एक लंबे स्ट्रिंग के रूप में माना जाएगा।

XYZ एक परिवर्तनशील उदाहरण हो सकता है $replacement, और यहां sed का उपयोग न करने का एक फायदा यह है कि आपको इस बात की चिंता नहीं है कि खोज या प्रतिस्थापित स्ट्रिंग में sed पैटर्न सीमांकक वर्ण (आमतौर पर, लेकिन आवश्यक नहीं, /) हो सकता है। एक नुकसान नियमित अभिव्यक्ति या किसी भी सेड के अधिक परिष्कृत संचालन का उपयोग करने में सक्षम नहीं हो रहा है।


टैब वर्णों के साथ इसका उपयोग करने के लिए कोई सुझाव? किसी कारण से मेरी स्क्रिप्ट इस पद्धति से बहुत भागने के साथ sed से बदलने के बाद टैब के साथ कुछ भी नहीं पाती है।
ब्रायन हैने

2
यदि आप स्ट्रिंग में एक टैब लगाना चाहते हैं, तो आप बैश के "डॉलर वाले एकल उद्धरण" वाक्यविन्यास के साथ ऐसा कर सकते हैं, इसलिए एक टैब $ 't' द्वारा दर्शाया जाता है, और आप $ echo 'टैब' $ कर सकते हैं '\ t''separated'> testfile; $ file_contents = $ (<testfile); $ गूंज "$ {file_contents // $ '\ t' / TAB}"; tabTABseparated `
johnraff


5

निम्नलिखित शेल कमांड आज़माएं:

find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

4
यह एक उत्कृष्ट उत्तर है कि "मैं सभी उपनिर्देशिकाओं में गलती से सभी फ़ाइलों को कैसे करूं", लेकिन यहां जो पूछा गया है वह प्रतीत नहीं होता है।
ट्रिपलए

यह सिंटैक्स BSD संस्करण के लिए काम नहीं करेगा sed, sed -i''इसके बजाय उपयोग करें ।
kenorb

5

गैर-अंतःक्रियात्मक रूप से फ़ाइल में पाठ को संपादित करने के लिए, आपको इन-प्लेस टेक्स्ट एडिटर जैसे विम की आवश्यकता है।

यहाँ सरल उदाहरण है कि इसे कमांड लाइन से कैसे उपयोग किया जाए:

vim -esnc '%s/foo/bar/g|:wq' file.txt

यह पूर्व संपादक के @slim उत्तर के बराबर है जो मूल रूप से एक ही बात है।

यहाँ कुछ exव्यावहारिक उदाहरण हैं।

पाठ की जगह fooके साथ barफ़ाइल में:

ex -s +%s/foo/bar/ge -cwq file.txt

एकाधिक फ़ाइलों के लिए अनुगामी व्हाट्सएप हटाना:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

समस्या निवारण (जब टर्मिनल अटक गया हो):

  • -V1क्रिया संदेश दिखाने के लिए परम जोड़ें ।
  • सेना द्वारा छोड़ने: -cwq!

यह सभी देखें:


अंतःक्रियात्मक रूप से प्रतिस्थापन करना चाहते थे। इसलिए "vim -esnc '% s / foo / bar / gc | की कोशिश की: wq' file.txt"। लेकिन टर्मिनल अभी अटका हुआ है। हम बैश शेल के बिना अजीब तरीके से व्यवहार के बिना प्रतिस्थापन को कैसे बना सकते हैं।
सिरेश

डिबग, जोड़ने -V1, छोड़ने के लिए मजबूर करने के लिए, उपयोग करें wq!
केनोरब

2

आप बैश स्क्रिप्ट के भीतर भी अजगर का उपयोग कर सकते हैं। मुझे यहाँ कुछ शीर्ष उत्तरों के साथ बहुत सफलता नहीं मिली, और यह पाया कि छोरों की आवश्यकता के बिना काम करना है:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

1

आप rpl कमांड का उपयोग कर सकते हैं। उदाहरण के लिए आप पूरे php प्रोजेक्ट में डोमेन नाम बदलना चाहते हैं।

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

यह स्पष्ट कारण नहीं है, लेकिन यह बहुत ही त्वरित और उपयोगी है। :)

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