फ़ाइल में केवल पहली घटना को बदलने के लिए sed का उपयोग कैसे करें?


217

मैं किसी भी मौजूदा #includes से पहले एक अतिरिक्त शामिल निर्देश के साथ C ++ स्रोत फ़ाइलों की एक बड़ी संख्या को अपडेट करना चाहूंगा। इस तरह के कार्य के लिए, मैं आम तौर पर फ़ाइल को फिर से लिखने के लिए sed के साथ एक छोटी बैश स्क्रिप्ट का उपयोग करता हूं।

मुझे sedहर घटना को प्रतिस्थापित करने के बजाय फ़ाइल में स्ट्रिंग की पहली घटना को कैसे बदलना है?

अगर मैं उपयोग करता हूं

sed s/#include/#include "newfile.h"\n#include/

यह सभी #includes को प्रतिस्थापित करता है।

समान चीज़ को प्राप्त करने के लिए वैकल्पिक सुझावों का भी स्वागत है।

जवाबों:


135
 # sed script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

या, यदि आप पसंद करते हैं: संपादक का नोट: केवल GNU के साथ काम करता है sed

sed '0,/foo/s//bar/' file 

स्रोत


86
मुझे लगता है कि मैं 'या यदि आप पसंद करते हैं' समाधान पसंद करते हैं। उत्तरों की व्याख्या करना भी अच्छा होगा - और उत्तर को सीधे प्रश्न का उत्तर देना और उसके बाद सामान्यीकरण करना, केवल सामान्यीकरण के बजाय। लेकिन अच्छा जवाब।
जोनाथन लेफलर

7
मैक उपयोगकर्ताओं के लिए FYI, आपको 0 को 1 से बदलना है, इसलिए: sed '1, / RE / s // to_that /' फ़ाइल
mhost

1
@mhost नहीं, यह प्रति पंक्ति # 1 में पाया जाता है और पैटर्न अन्य पंक्ति में है या नहीं, यह फिर भी प्रतिस्थापित नहीं होगा, लेकिन फिर भी यह पहला पाया गया पैटर्न है। PS यह उल्लेख किया जाना चाहिए कि यह 0,केवल साथ काम करता हैgnu sed
Jotne

11
क्या कोई कृपया 'या यदि आप पहले से समाधान' की व्याख्या कर सकते हैं? मुझे नहीं पता कि "पैटर्न" कहां से लाना है।
१२:४४ पर जीन-ल्यूक नसिफ कोल्हो

3
@ जीन- LucNacifCoelho: का उपयोग करना s//- यानी, एक खाली रेगेक्स - इसका मतलब है कि सबसे हाल ही में लागू रेगेक्स का पुन: उपयोग किया जाता है; इस मामले में, RE। इस सुविधाजनक शॉर्टकट का मतलब है कि आपको अपने sकॉल में रेंज-एंडिंग रेगेक्स की नकल नहीं करनी है ।
mklement0

289

एक sedस्क्रिप्ट जो "केले" द्वारा "ऐप्पल" की पहली घटना को बदल देगी

उदाहरण

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

यह सरल स्क्रिप्ट है: संपादक का नोट: केवल जीएनयू के साथ काम करता है sed

sed '0,/Apple/{s/Apple/Banana/}' input_filename

पहले दो पैरामीटर 0और /Apple/सीमा निर्दिष्ट हैं। वह s/Apple/Banana/है जो उस सीमा के भीतर निष्पादित किया जाता है। तो इस मामले में "शुरुआत की सीमा के भीतर ( 0) की पहली आवृत्ति तक Apple, के Appleसाथ प्रतिस्थापित करें Banana। केवल पहले Appleको प्रतिस्थापित किया जाएगा।

बैकग्राउंड: पारंपरिक sedमें रेंज स्पेसियर भी "यहां शुरू होता है" और "यहां समाप्त होता है" (समावेशी)। हालांकि सबसे कम "शुरू" पहली पंक्ति (पंक्ति 1) है, और अगर "अंत यहाँ" एक रेगेक्स है, तो इसे केवल "शुरू" के बाद अगली पंक्ति के खिलाफ मैच करने का प्रयास किया जाता है, इसलिए जल्द से जल्द संभव रेखा रेखा है 2. इसलिए चूंकि सीमा समावेशी है, सबसे छोटी संभव सीमा "2 लाइनें" है और सबसे छोटी शुरुआत सीमा दोनों रेखाएं 1 और 2 हैं (अर्थात यदि पंक्ति 1 पर कोई घटना होती है, तो पंक्ति 2 पर होने वाली घटनाओं को भी बदल दिया जाएगा, इस मामले में वांछित नहीं है )। GNUसीड "छद्म" के रूप में निर्दिष्ट शुरू करने की अनुमति देने के अपने स्वयं के विस्तार को जोड़ता है line 0ताकि सीमा का अंत हो सके line 1, यह "केवल पहली पंक्ति" की सीमा को अनुमति देता है

या एक सरलीकृत संस्करण (एक खाली आरई जैसा //मतलब है कि इसके पहले निर्दिष्ट एक को फिर से उपयोग करना है, इसलिए यह समकक्ष है):

sed '0,/Apple/{s//Banana/}' input_filename

और घुंघराले ब्रेसिज़ कमांड के लिए वैकल्पिक हैं s, इसलिए यह भी बराबर है:

sed '0,/Apple/s//Banana/' input_filename

ये सभी sedकेवल जीएनयू पर काम करते हैं ।

आप होमबॉव का उपयोग करके ओएस एक्स पर जीएनयू सेड भी स्थापित कर सकते हैं brew install gnu-sed


166
मानव भाषा में अनुवादित: पंक्ति 0 से शुरू करें, तब तक जारी रखें जब तक आप 'Apple' से मेल नहीं खाते, घुंघराले ब्रैकेट में प्रतिस्थापन को निष्पादित करें। cfr: grymoire.com/Unix/Sed.html#uh-29
मारीतोमो

8
OS X पर, मुझे मिलता हैsed: 1: "…": bad flag in substitute command: '}'
ELLIOTTCABLE

5
ओएस एक्स पर @ELLIOTTCABLE का उपयोग करें sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'। @ मिखाइलवीएस के जवाब (वर्तमान में) से नीचे का रास्ता।
djb

8
कोष्ठक के बिना भी काम करता है:sed '0,/foo/s/foo/bar/'
इनोकेंटी

5
मुझे इसके sed: -e expression #1, char 3: unexpected साथ, ``
जोनाथन

56
sed '0,/pattern/s/pattern/replacement/' filename

यह मेरे लिए काम किया।

उदाहरण

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

संपादक का ध्यान: दोनों ही GNU के साथ काम करते हैं sed


2
@ यह अभी भी अन्य लाइनों में भी उदाहरण बदलता है; न सिर्फ पहला उदाहरण
सैराट

1
@ सरस हां, आप सही कह रहे हैं। sed '1,/pattern/s/pattern/replacement/' filenameकेवल अगर मैक पर "पैटर्न पहली पंक्ति में नहीं होगा" काम करता है। मैं अपनी पिछली टिप्पणी हटा दूंगा क्योंकि यह सटीक नहीं है। विवरण यहां पाया जा सकता है ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… )। एंडी का जवाब केवल GNU sed के लिए काम करता है, लेकिन मैक पर नहीं।
लैंड्स

45

एक सिंहावलोकन कई उपयोगी की मौजूदा जवाब , से पूरित स्पष्टीकरण :

यहां के उदाहरण एक सरलीकृत उपयोग के मामले का उपयोग करते हैं: 'फू' शब्द को 'बार' के साथ केवल पहली मिलान पंक्ति में बदलें।
के उपयोग के कारण एएनएसआई सी उद्धृत तार ( $'...') नमूना इनपुट लाइनों प्रदान करने के लिए, bash, ksh, या zshशेल के रूप में माना जाता है।


sedकेवल GNU :

बेन हॉफस्टीन का अन्वेषक हमें दिखाता है कि GNU , POSIX विनिर्देश को एक विस्तार प्रदान करता है जिसके लिए निम्नलिखित 2-पता फ़ॉर्म की अनुमति देता है : ( यहाँ एक मनमाना नियमित अभिव्यक्ति का प्रतिनिधित्व करता है)।sed0,/re/re

0,/re/रेगेक्स को बहुत पहले लाइन पर भी मैच करने की अनुमति देता है । दूसरे शब्दों में: ऐसा पता पहली पंक्ति से एक रेखा बनाएगा जिसमें पंक्ति और उस रेखा शामिल है जो मेल खाती है re- चाहे reवह पहली पंक्ति में हो या किसी भी बाद की रेखा पर।

  • POSIX अनुरूप फार्म के साथ इस कंट्रास्ट 1,/re/, जो एक सीमा बनाता है कि 1 लाइन से मैच के लिए और लाइन सहित कि मैचों reपर बाद में लाइनों; दूसरे शब्दों में: यह पहली पंक्ति में होने वाले मैच की पहली घटना का पता नहीं लगाएगाre और हाल ही में उपयोग किए गए रीगेक्स// के पुन: उपयोग के लिए शॉर्टहैंड के उपयोग को भी रोकता है (अगला बिंदु देखें)। 1

यदि आप एक 0,/re/पते s/.../.../(प्रतिस्थापन) के साथ एक पते को जोड़ते हैं जो समान नियमित अभिव्यक्ति का उपयोग करता है , तो आपकी कमांड प्रभावी रूप से केवल पहली पंक्ति पर प्रतिस्थापन का प्रदर्शन करेगी जो मेल खाती है reसबसे हाल ही में लागू नियमित अभिव्यक्ति के पुन: उपयोग के लिए
sed एक सुविधाजनक शॉर्टकट प्रदान करता है : एक खाली सीमांत जोड़ी// ,।

$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo' 
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

POSIX-features-only sedजैसे BSD (macOS)sed ( GNU के साथ भी काम करेगा sed):

चूंकि 0,/re/इसका उपयोग नहीं किया जा सकता है और प्रपत्र 1,/re/का पता नहीं चलेगा reयदि यह पहली पंक्ति में होता है (ऊपर देखें), तो पहली पंक्ति के लिए विशेष हैंडलिंग आवश्यक है

मिखाइलव्स के जवाब में इस तकनीक का उल्लेख है, यहां एक ठोस उदाहरण दिया गया है:

$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

ध्यान दें:

  • खाली रेगेक्स //शॉर्टकट को यहां दो बार नियोजित किया गया है: एक बार सीमा के समापन बिंदु के लिए, और एक बार sकॉल में; दोनों ही मामलों में, रेगेक्स fooका पुन: उपयोग किया जाता है, जिससे हमें इसे डुप्लिकेट नहीं करना पड़ता है, जो छोटे और अधिक रखरखाव कोड दोनों के लिए बनाता है।

  • POSIX sedको कुछ कार्यों के बाद वास्तविक नईलाइन्स की आवश्यकता होती है, जैसे कि लेबल के नाम पर या यहां तक ​​कि इसके चूक के बाद भी, जैसा कि tयहाँ है; रणनीतिक रूप से स्क्रिप्ट को कई -eविकल्पों में विभाजित करना एक वास्तविक newlines का उपयोग करने का एक विकल्प है: प्रत्येक -eस्क्रिप्ट को समाप्त करना जहां एक नई पंक्ति को सामान्य रूप से जाने की आवश्यकता होगी।

1 s/foo/bar/fooकेवल 1 पंक्ति पर प्रतिस्थापित करता है, अगर वहाँ पाया जाता है। यदि हां, तो tस्क्रिप्ट के अंत तक शाखाएं (रेखा पर शेष कमांड को छोड़ती हैं)। ( tकिसी लेबल पर फ़ंक्शन शाखाएं तभी होती हैं जब सबसे हालिया sकॉल ने वास्तविक प्रतिस्थापन का प्रदर्शन किया हो; लेबल की अनुपस्थिति में, जैसा कि यहां है, स्क्रिप्ट का अंत शाखित है)।

जब ऐसा होता है, तो सीमा पता 1,//, जो आम तौर पर लाइन 2 से शुरू होने वाली पहली घटना का पता लगाता है , मैच नहीं करेगा , और सीमा को संसाधित नहीं किया जाएगा, क्योंकि वर्तमान लाइन के पहले से ही पते का मूल्यांकन किया जाता है 2

इसके विपरीत, ऐसे 1 लाइन पर कोई मुकाबला नहीं है, तो 1,// होगा प्रवेश किया, और सच पहला मैच मिल जाएगा।

शुद्ध प्रभाव जीएनयू साथ के रूप में ही है sedकी 0,/re/केवल पहली घटना बदल दिया जाता है, चाहे वह 1 लाइन या किसी अन्य पर होता है:।


गैर-रेंज दृष्टिकोण

Potong के जवाब दर्शाता है पाश तकनीक है कि एक की जरूरत महसूस की बाईपास ; चूंकि वह GNU sed सिंटैक्स का उपयोग करता है , यहाँ POSIX- आज्ञाकारी समकक्ष हैं :

लूप तकनीक 1: पहले मैच पर, प्रतिस्थापन प्रदर्शन करें, फिर एक लूप दर्ज करें जो बस शेष रेखाओं को प्रिंट करता है :

$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

केवल छोटी फ़ाइलों के लिए लूप तकनीक 2 : पूरे इनपुट को मेमोरी में पढ़ें, फिर उस पर एकल प्रतिस्थापन करें

$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

1 1.61803 उदाहरण देता है कि क्या होता है 1,/re/, इसके साथ और उसके बाद क्या होता है s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'पैदावार $'1bar\n2bar'; यानी, दोनों लाइनें अपडेट की गईं, क्योंकि लाइन नंबर 11 लाइन से मेल खाता है, और रेगेक्स /foo/- रेंज का अंत - उसके बाद केवल अगली लाइन पर शुरू करने के लिए देखा जाता है । इसलिए, इस मामले में दोनों पंक्तियों का चयन किया जाता है, और s/foo/bar/प्रतिस्थापन दोनों पर किया जाता है।
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' विफल रहता है : sed: first RE may not be empty(BSD / macOS) और sed: -e expression #1, char 0: no previous regular expression(GNU) के साथ, क्योंकि, उस समय पहली पंक्ति पर कार्रवाई की जा रही है (लाइन संख्या 1शुरू होने के कारण ), कोई भी रेगेक्स अभी तक लागू नहीं किया गया है, इसलिए//कुछ भी संदर्भित नहीं करता है।
जीएनयू sedके विशेष 0,/re/सिंटैक्स के अपवाद के साथ , कोई भी रेखा जो लाइन नंबर से शुरू होती है , प्रभावी रूप से उपयोग को रोकती है //


25

आप कुछ समान करने के लिए awk का उपयोग कर सकते हैं।

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

स्पष्टीकरण:

/#include/ && !done

{} के बीच एक्शन स्टेटमेंट चलाता है जब लाइन "#include" से मेल खाती है और हमने इसे पहले ही संसाधित नहीं किया है।

{print "#include \"newfile.h\""; done=1;}

यह "newfile.h" # प्रिंट करता है, हमें उद्धरणों से बचने की आवश्यकता है। फिर हमने किए गए चर को 1 पर सेट किया है, इसलिए हम अधिक शामिल नहीं करते हैं।

1;

इसका मतलब है "प्रिंट आउट द लाइन" - $ 0 प्रिंट करने के लिए एक खाली एक्शन डिफॉल्ट, जो पूरी लाइन को प्रिंट करता है। एक लाइनर और sed IMO से समझने में आसान :-)


4
यह उत्तर sed समाधानों की तुलना में अधिक पोर्टेबल है, जो ग्नू sed आदि पर भरोसा करते हैं .. (जैसे ओएस-एक्स चूसना में sed)!
जे टेलर टेलर

1
यह वास्तव में अधिक समझने योग्य है, लेकिन मेरे लिए यह जगह की जगह एक पंक्ति जोड़ता है; आदेश का इस्तेमाल किया: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
अनाज हत्यारा

यहाँ वही, सबसे समझने योग्य आदेश, लेकिन यह इसे बदलने के बजाय पाए गए स्ट्रिंग के ऊपर एक रेखा जोड़ता है
Flion

1
उत्तर काफी पठनीय है। यहां मेरा संस्करण है जो एक नई लाइन जोड़ने के बजाय स्ट्रिंग को बदलता है। awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
ओरसीरिस डी जोंग

18

Linuxtopia sed FAQ में उत्तरों का व्यापक संग्रह । यह इस बात पर भी प्रकाश डालता है कि लोगों द्वारा दिए गए कुछ उत्तर गैर-जीएनयू संस्करण के साथ काम नहीं करेंगे, जैसे कि सेड

sed '0,/RE/s//to_that/' file

गैर-जीएनयू संस्करण में होना चाहिए

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

हालाँकि, यह संस्करण gnu sed के साथ काम नहीं करेगा।

यहाँ एक संस्करण है जो दोनों के साथ काम करता है:

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

उदाहरण के लिए:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename

दरअसल, उबंटू लिनक्स v16 और फ्रीबीएसडी v10.2 पर काम कर रहा है। धन्यवाद।
सोलापाजो डी एरियेरेज़

12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

यह स्क्रिप्ट कैसे काम करती है: 1 और पहले #include(लाइन 1 के बाद) के बीच की रेखाओं के लिए , यदि रेखा के साथ शुरू होता है #include, तो निर्दिष्ट लाइन को पूर्व निर्धारित करें।

हालाँकि, यदि पहली #includeपंक्ति 1 में है, तो दोनों पंक्ति 1 और उसके बाद के अगले भाग में #includeपूर्ववर्ती रेखा होगी। यदि आप GNU का उपयोग कर रहे हैं sed, तो इसका एक विस्तार है जहाँ 0,/^#include/(के बजाय 1,) सही काम करेगा।


11

बस अंत में घटना की संख्या जोड़ें:

sed s/#include/#include "newfile.h"\n#include/1

7
दुर्भाग्य से, यह काम नहीं करता है। यह फ़ाइल की प्रत्येक पंक्ति पर सिर्फ पहली घटना को प्रतिस्थापित करता है न कि फ़ाइल में पहली घटना को।
डेविड डिबेन

1
इसके अतिरिक्त, यह एक GNU sed एक्सटेंशन है, न कि एक मानक sed सुविधा।
जोनाथन लेफ्लर

10
हम्मम ... समय बीत जाता है। POSIX 2008/2013 के लिए sedस्थानापन्न कमांड को निर्दिष्ट करता है: [2addr]s/BRE/replacement/flagsऔर नोट करता है कि "झंडे का मूल्य शून्य या उससे अधिक होगा: n केवल स्थान के भीतर पाए गए BRE के nth घटना के लिए स्थानापन्न।" इस प्रकार, कम से कम POSIX 2008 में, अनुगामी 1GNU sedएक्सटेंशन नहीं है । वास्तव में, यहां तक ​​कि SUS / POSIX 1997 मानक में भी, यह समर्थित था, इसलिए मैं 2008 में बुरी तरह से लाइन से बाहर हो गया था।
जोनाथन लेफ़लर

7

एक संभावित समाधान:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :a
    n
    ba

स्पष्टीकरण:

  • जब तक हम #include नहीं पाते तब तक लाइनें पढ़ें, इन पंक्तियों को प्रिंट करें फिर नया चक्र शुरू करें
  • नई सम्मिलित लाइन डालें
  • एक लूप दर्ज करें जो सिर्फ लाइनों को पढ़ता है (डिफ़ॉल्ट रूप से इन लाइनों को भी प्रिंट करेगा), हम यहां से स्क्रिप्ट के पहले भाग में वापस नहीं आएंगे

sed: file me4.sed line 4: ":" lacks a label
रोज़रपैक

जाहिरा तौर पर हाल के सीड संस्करण में कुछ बदल गया है और खाली लेबल की अनुमति नहीं है। उत्तर को अद्यतन किया गया
मित्नाकुल

4

मुझे पता है कि यह एक पुरानी पोस्ट है लेकिन मेरे पास एक समाधान था जिसका मैं उपयोग करता था:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

मूल रूप से पहली घटना को प्रिंट करने और वहां रुकने के लिए grep का उपयोग करें। इसके अतिरिक्त प्रिंट लाइन नंबर अर्थात 5:line। पाइप जो sed में और हटा दें: और कुछ भी इसके बाद आपको बस एक पंक्ति संख्या के साथ छोड़ दिया जाता है। सीड में पाइप जो s /.*/ को अंतिम संख्या में जोड़ता है, जिसके परिणामस्वरूप 1 लाइन स्क्रिप्ट आती है जिसे फाइल पर स्क्रिप्ट के रूप में चलाने के लिए अंतिम सेड में पाइप किया जाता है।

इसलिए यदि regex = #includeऔर प्रतिस्थापित = blahऔर पहली घटना grep ढूँढता है पंक्ति 5 पर है, तो अंतिम सेड पर लगा डेटा होगा 5s/.*/blah/

भले ही पहली घटना पहली पंक्ति पर हो।


मैं पूरी तरह से मल्टीलाइन सेड स्क्रिप्ट या किसी भी चीज़ के साथ सेड कमांड से नफरत करता हूं, लेकिन इस दृष्टिकोण के साथ मैं बोर्ड पर हूं। यहाँ मैंने अपने usecase (bash के साथ काम करता है) के लिए प्रयोग किया है: filepath = / etc / मेजबान; patt = '^ \ (127 \ _ \ _ \ _ .1। * \)'; उत्तर = '\ 1 नयाहोस्टालिया'; sed $ (IFS =: linearray = ($ (grep -E -m 1 -n "$ patt" "$ filepath")) && गूंज $ {linearray [0]}) s / "$ patt" / "$ repl" / "$ filepath"
parity3

यह काम करता हैं। हालांकि केवल sed के साथ यह स्वीकार करने के लिए पर्याप्त स्मार्ट हैं कि sed -f -कुछ नहीं हैं, लेकिन आप इसके चारों ओर काम कर सकते हैं :)
rogerdpack

3

यदि सभी लाइनों में पहली घटना के लिए किसी चरित्र को बदलने के लिए यहां आया था (जैसे खुद), इसका उपयोग करें:

sed '/old/s/old/new/1' file

-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12

उदाहरण के लिए 1 से 2 को बदलकर, आप इसके बजाय केवल दूसरे का स्थान ले सकते हैं।


2
आपको यह सब करने की ज़रूरत नहीं है, 's/a/b/'साधन match a, औरdo just first match for every matching line
सैमवेन

मैं समवीन के साथ हूं। साथ ही यह यहां पूछे गए सवाल का जवाब नहीं देता है । मैं इस उत्तर को हटाने की सलाह दूंगा।
सोकोवई

सवाल था "एक फाइल में पहली घटना" एक लाइन में पहली घटना नहीं "
मैनुअल रोमेरो

3

GNU sed के -zविकल्प के साथ आप पूरी फाइल को प्रोसेस कर सकते हैं जैसे कि यह केवल एक लाइन थी। इस तरह s/…/…/से पूरी फ़ाइल में केवल पहले मैच की जगह होगी। याद रखें: s/…/…/केवल प्रत्येक पंक्ति में पहले मैच की जगह लेता है, लेकिन -zविकल्प के साथ sedपूरी फाइल को एक पंक्ति के रूप में मानता है।

sed -z 's/#include/#include "newfile.h"\n#include'

सामान्य स्थिति में आपको अपनी sed अभिव्यक्ति को फिर से लिखना होगा क्योंकि पैटर्न स्पेस अब केवल एक लाइन के बजाय पूरी फाइल रखती है। कुछ उदाहरण:

  • s/text.*//के रूप में फिर से लिखा जा सकता है s/text[^\n]*//। न्यूलाइन चरित्र को छोड़कर[^\n] सब कुछ मेल खाता है । तब तक सभी प्रतीकों का मिलान किया जाएगा जब तक कि एक नई रेखा नहीं मिल जाती।[^\n]*text
  • s/^text//के रूप में फिर से लिखा जा सकता है s/(^|\n)text//
  • s/text$//के रूप में फिर से लिखा जा सकता है s/text(\n|$)//

2

मैं इसे एक अजीब स्क्रिप्ट के साथ करूँगा:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}    
END {}

फिर इसे awk के साथ चलाएं:

awk -f awkscript headerfile.h > headerfilenew.h

मैला हो सकता है, मैं इस के लिए नया हूँ।


2

एक वैकल्पिक सुझाव के रूप में आप edकमांड को देखना चाहते हैं ।

man 1 ed

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   /# *include/i
   #include "newfile.h"
   .
   ,p
   q
EOF

2

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

        sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
            production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter

यह केवल पहली घटना को बदलता है।

${nowms}एक पर्ल स्क्रिप्ट द्वारा निर्धारित मिलीसेकंड में समय है, स्क्रिप्ट के $counterभीतर लूप नियंत्रण के लिए उपयोग किया जाने वाला एक काउंटर है, \जो कमांड को अगली पंक्ति पर जारी रखने की अनुमति देता है।

फ़ाइल को पढ़ा जाता है और stdout को किसी कार्य फ़ाइल पर पुनर्निर्देशित किया जाता है।

जिस तरह से मैं इसे समझता हूं, 1,/====RSSpermalink====/वह सीम को बताता है कि कब सीमा सीमित करके रोकना है, और फिर s/====RSSpermalink====/${nowms}/पहले स्ट्रिंग को दूसरे के साथ बदलने के लिए परिचित सेड कमांड है।

मेरे मामले में मैंने कमांड को दोहरे उद्धरण चिह्नों में डाल दिया है क्योंकि मैं इसे बैश स्क्रिप्ट में चर के साथ उपयोग कर रहा हूं।


2

FreeBSD का उपयोग edऔर संसाधित edनहीं होने की स्थिति में त्रुटि का "कोई मिलान नहीं" होने से बचें include:

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917 
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   ,g/# *include/u\
   u\
   i\
   #include "newfile.h"\
   .
   ,p
   q
EOF

यह उल्लेखनीय रूप से टिमो के उत्तर के समान है लेकिन एक साल बाद जोड़ा गया।
जोनाथन लेफ्लर 3

2

यह आपके लिए काम कर सकता है (GNU sed):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

या यदि स्मृति कोई समस्या नहीं है:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...

0

निम्न आदेश एक फ़ाइल के भीतर एक स्ट्रिंग की पहली घटना को हटाता है। यह खाली लाइन को भी हटा देता है। यह एक xml फ़ाइल पर प्रस्तुत किया गया है, लेकिन यह किसी भी फ़ाइल के साथ काम करेगा।

उपयोगी है यदि आप xml फ़ाइलों के साथ काम करते हैं और आप एक टैग निकालना चाहते हैं। इस उदाहरण में यह "टैग" टैग की पहली घटना को हटाता है।

कमान:

sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//}  -e 's/ *$//' -e  '/^$/d'  source.txt > output.txt

स्रोत फ़ाइल (source.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <isTag>false</isTag>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

परिणाम फ़ाइल (output.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

पीएस: यह सोलारिस सनोस 5.10 (काफी पुराना) पर मेरे लिए काम नहीं करता था, लेकिन यह लिनक्स 2.6, सेड संस्करण 4.1.5 पर काम करता है


यह पिछले जवाबों की संख्या के समान मूल विचार की तरह उल्लेखनीय रूप से दिखता है, एक ही चेतावनी के साथ कि यह केवल जीएनयू के साथ काम करता है sed(इसलिए यह सोलारिस के साथ काम नहीं करता था)। आपको इसे हटा देना चाहिए, कृपया - यह वास्तव में एक प्रश्न के लिए विशिष्ट नई जानकारी प्रदान नहीं करता है जो आपके जवाब देने पर पहले से ही this please वर्ष पुराना था। दी गई है, यह एक काम किया उदाहरण है, लेकिन जब यह एक के रूप में कई जवाब है सवाल बहस का मूल्य है।
जोनाथन लेफ्लर 3

0

नया कुछ नहीं, लेकिन शायद थोड़ा और ठोस जवाब: sed -rn '0,/foo(bar).*/ s%%\1%p'

उदाहरण: xwininfo -name unity-launcherजैसे उत्पादन का उत्पादन:

xwininfo: Window id: 0x2200003 "unity-launcher"

  Absolute upper-left X:  -2980
  Absolute upper-left Y:  -198
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 2880
  Height: 98
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 0
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: ForgetGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +-2980+-198  -2980+-198  -2980-1900  +-2980-1900
  -geometry 2880x98+-2980+-198

xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'उत्पादन के साथ खिड़की आईडी निकालना :

0x2200003

0

POSIXly (sed में भी मान्य), केवल एक रेगीक्स का उपयोग किया जाता है, केवल एक लाइन के लिए मेमोरी की आवश्यकता होती है (हमेशा की तरह):

sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'

व्याख्या की:

sed '
/\(#include\).*/!b          # Only one regex used. On lines not matching
                            # the text  `#include` **yet**,
                            # branch to end, cause the default print. Re-start.
//{                         # On first line matching previous regex.
    h                       # hold the line.
    s//\1 "newfile.h"/      # append ` "newfile.h"` to the `#include` matched.
    G                       # append a newline.
  }                         # end of replacement.
:1                          # Once **one** replacement got done (the first match)
n                           # Loop continually reading a line each time
b1                          # and printing it by default.
'                           # end of sed script.

0

उपयोग का मामला शायद यह हो सकता है कि आपकी घटनाएं आपकी फ़ाइल में फैली हुई हैं, लेकिन आप जानते हैं कि आपकी एकमात्र चिंता पहले 10, 20 या 100 लाइनों में है।

फिर बस उन पंक्तियों को स्वीकार करने से समस्या ठीक हो जाती है - भले ही ओपी का शब्दांकन केवल पहले का संबंध हो।

sed '1,10s/#include/#include "newfile.h"\n#include/'

0

स्रोत फ़ाइलों में उल्लेख किए बिना हेडर को शामिल करने के लिए एक संभावित समाधान संकलक को बता सकता है। GCC में ये विकल्प हैं:

   -include file
       Process file as if "#include "file"" appeared as the first line of
       the primary source file.  However, the first directory searched for
       file is the preprocessor's working directory instead of the
       directory containing the main source file.  If not found there, it
       is searched for in the remainder of the "#include "..."" search
       chain as normal.

       If multiple -include options are given, the files are included in
       the order they appear on the command line.

   -imacros file
       Exactly like -include, except that any output produced by scanning
       file is thrown away.  Macros it defines remain defined.  This
       allows you to acquire all the macros from a header without also
       processing its declarations.

       All files specified by -imacros are processed before all files
       specified by -include.

Microsoft के कंपाइलर में / FI (मजबूर शामिल) विकल्प है।

यह सुविधा कुछ सामान्य हेडर के लिए उपयोगी हो सकती है, जैसे प्लेटफ़ॉर्म कॉन्फ़िगरेशन। लिनक्स कर्नेल का मेकफाइल इसके लिए उपयोग करता -includeहै।


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