एक स्ट्रिंग बदलें जिसमें newline वर्ण हों


10

साथ bashनिम्नलिखित लोगों की तरह पंक्तियों के साथ खोल, एक फ़ाइल में

first "line"
<second>line and so on

मैं हर बार एक या अधिक घटनाओं को प्रतिस्थापित "line"\n<second>करना other charactersऔर प्राप्त करना चाहूंगा :

first other characters line and so on

इसलिए मुझे एक स्ट्रिंग को विशेष वर्णों जैसे कि "और <एक नए वर्ण के साथ बदलना होगा ।

अन्य उत्तरों के बीच खोज करने के बाद, मैंने पाया कि sedकमांड के राइट-हैंड साइड (इसलिए, other charactersस्ट्रिंग) में नए सिरे से स्वीकार कर सकता है , लेकिन बाएं में नहीं।

वहाँ एक तरीका है ( इस से अधिक सरल ) के साथ इस परिणाम प्राप्त करने के लिए sedया grep?


तुम w / एक काम कर रहे हैं MAC? \newline बयान आप कर यही कारण है कि मैं पूछना। लोग शायद ही कभी पूछते हैं कि क्या s//\n/आप GNU के साथ जैसा कर सकते हैं sed, हालांकि अधिकांश दूसरे sedलोग उस दाहिने हाथ की तरफ भागने को अस्वीकार कर देंगे। फिर भी, \nपलायन किसी भी POSIX में बाईं ओर काम करेगा sedऔर आप उन्हें आंशिक रूप से अनुवाद कर सकते हैं जैसे y/c/\n/कि इसका प्रभाव उतना ही होगा s/c/\n/gऔर इसलिए हमेशा उपयोगी नहीं होगा।
24

जवाबों:


3

तीन अलग-अलग sedकमांड:

sed '$!N;s/"[^"]*"\n<[^>]*>/other characters /;P;D'

sed -e :n -e '$!N;s/"[^"]*"\n<[^>]*>/other characters /;tn'

sed -e :n -e '$!N;/"$/{$!bn' -e '};s/"[^"]*"\n<[^>]*>/other characters /g'

वे तीनों बुनियादी s///सर्वहाराकरण आदेश पर निर्माण करते हैं :

s/"[^"]*"\n<[^>]*>/other characters /

वे सभी अंतिम पंक्ति के अपने संचालन में सावधानी बरतने की कोशिश sedकरते हैं , क्योंकि वे किनारे के मामलों में अपने आउटपुट पर भिन्न होते हैं। इसका अर्थ यह है $!कि हर पंक्ति से मेल खाता एक पता है !जो $अंतिम नहीं है ।

वे सभी Nएक \nएक्सलाइन चरित्र का अनुसरण करते हुए अगली इनपुट लाइन को पैटर्न स्पेस में जोड़ने के लिए एक्सट्रीम कमांड का भी उपयोग करते हैं । किसी को भी जो sedथोड़ी देर के लिए किया गया है, ने \newline वर्ण पर भरोसा करना सीख लिया होगा - क्योंकि एक को प्राप्त करने का एकमात्र तरीका स्पष्ट रूप से इसे वहां रखना है।

कार्रवाई करने से पहले जितना संभव हो उतना कम इनपुट में तीनों पढ़ने का प्रयास करते हैं - sedऐसा करने से पहले पूरी इनपुट फ़ाइल में पढ़ने की जरूरत नहीं है।

हालांकि वे सभी करते हैं N, वे तीनों अपने पुनरावृत्ति के तरीकों में भिन्न हैं।

पहले कमान

पहला आदेश एक बहुत ही सरल N;P;Dलूप को नियोजित करता है । ये तीन कमांड किसी भी POSIX- संगत के लिए अंतर्निहित हैं sedऔर वे एक दूसरे के पूरक हैं।

  • N- जैसा कि पहले ही उल्लेख किया गया है, Nएक सम्मिलित \nईवलाइन सीमांकक के बाद पैटर्न-स्पेस में एक्सट्रीम इनपुट लाइन को जोड़ता है।
  • P- जैसे p; यह Pपैटर्न-स्पेस का संकेत देता है - लेकिन केवल पहली होने वाली \nइलाइन वर्ण तक। और इसलिए, निम्नलिखित इनपुट / कमांड दिया गया है:

    • printf %s\\n one two | sed '$!N;P;d'
  • sed Pकेवल एक ही संकेत देता है । हालांकि, के साथ ...

  • D- जैसे d; यह Dपैटर्न-स्पेस को हटाता है और एक और लाइन-चक्र शुरू करता है। इसके विपरीत d , पैटर्न-स्पेस में Dकेवल पहली होने वाली \nईवलाइन तक ही डिलीट होती है। यदि \nईलाइन वर्ण के बाद पैटर्न-स्पेस में अधिक है , तो sedजो रहता है उसके साथ अगली पंक्ति चक्र शुरू होता है। यदि dपिछले उदाहरण में प्रतिस्थापित किया गया है D, उदाहरण के लिए, एक और दो दोनों sedको Pरिंट करेगा ।

यह कमांड केवल उन पंक्तियों के लिए पुनरावर्ती करता है जो ubsteration स्टेटमेंट से मेल नहीं खाती हैं s///। क्‍योंकि s///ऑब्‍स्ट्रेशन के \nसाथ जोड़ी गई ईवलाइन को हटा देता है N, sed Dईलेट्स पैटर्न-स्‍पेस होने पर कुछ भी शेष नहीं रहता है।

Pऔर / या Dचुनिंदा रूप से आवेदन करने के लिए टेस्ट किए जा सकते हैं , लेकिन अन्य कमांड हैं जो उस रणनीति के साथ बेहतर हैं। क्योंकि पुनरावृत्ति को लगातार लाइनों को संभालने के लिए लागू किया जाता है जो प्रतिस्थापन नियम के केवल एक हिस्से से मेल खाते हैं , ubsteration के दोनों सिरों से मेल खाती लाइनों के लगातार क्रम s///अच्छी तरह से काम नहीं करते हैं:

इस इनपुट को देखते हुए:

first "line"
<second>"line"
<second>"line"
<second>line and so on

... यह प्रिंट करता है ...

first other characters "line"
<second>other characters line and so on

हालांकि, यह संभालता है

first "line"
second "line"
<second>line

...बस ठीक।

दूसरी कमान

यह कमांड तीसरे के समान है। दोनों एक रेंच:b / tएस्‍ट लेबल लगाते हैं (जैसा कि जोसेफ आर के जवाब में यहां भी प्रदर्शित किया गया है ) और कुछ विशेष शर्तों के साथ इसे वापस लाते हैं।

  • -e :n -e- पोर्टेबल sedस्क्रिप्ट :या तो एक \nइवलाइन या एक नई इनलाइन -execution स्टेटमेंट के साथ एक लेबल की परिभाषा का परिसीमन करेगी ।
    • :n- नाम के एक लेबल को परिभाषित करता है n। इसे bnया तो किसी भी समय वापस किया जा सकता है tn
  • tn- tएस्टीम कमांड एक निर्दिष्ट लेबल पर वापस लौटता है (या, यदि कोई भी प्रदान नहीं किया गया है, तो वर्तमान लाइन-चक्र के लिए स्क्रिप्ट कोs/// क्विट करता है ) यदि लेबल को परिभाषित किया गया था या जब से इसे अंतिम रूप से tसफल कहा जाता था, तब से कोई भी ubsteration ।

इस कमांड में मिलान लाइनों के लिए पुनरावृत्ति होती है। यदि अन्यsed पैटर्न के साथ पैटर्न को सफलतापूर्वक बदल दिया जाता है , तो लेबल पर वापस जाता है और फिर से कोशिश करता है। यदि एक ऑबस्ट्रेशन को ऑटोप्रीन्ट्स पैटर्न-स्पेस नहीं किया जाता है और अगली पंक्ति-चक्र शुरू होता है।sed:ns///sed

यह लगातार दृश्यों को बेहतर तरीके से संभालता है। जहां पिछले एक असफल रहा, यह प्रिंट:

first other characters other characters other characters line and so on

तीसरी आज्ञा

जैसा कि उल्लेख किया गया है, यहां तर्क अंतिम के समान है, लेकिन परीक्षण अधिक स्पष्ट है।

  • /"$/bn- यह sedपरीक्षा है। क्योंकि bरैंच कमांड इस पते का एक फ़ंक्शन है, sedकेवल एक ईवलाइन को जोड़ने के बाद ही bरैंच वापस आ जाएगा और पैटर्न-स्पेस अभी भी दोहरे-उद्धरण के साथ समाप्त होता है ।:n\n"

इस तरह के बीच Nऔर bजितना संभव हो उतना कम किया जाता है - इस तरह sedसे यह सुनिश्चित करने के लिए बहुत जल्दी इनपुट प्राप्त कर सकता है कि निम्नलिखित लाइन आपके नियम से मेल नहीं खा सकती है। s///Ubstitution में है कि यह रोजगार यहां अलग है gऔर इसलिए यह एक बार में सभी आवश्यक प्रतिस्थापन करना होगा - lobal झंडा। समान इनपुट को देखते हुए यह कमांड अंतिम रूप से अनौपचारिक रूप से आउटपुट करता है।


तुच्छ प्रश्न के लिए क्षमा करें, लेकिन इसका क्या अर्थ है DATAऔर आप पाठ इनपुट कैसे प्राप्त करते हैं?
बोवपार्क

@BowPark - इस उदाहरण <<\DATA\ntext input\nDATA\nमें बेक किया गया है, लेकिन यह केवल sedएक दस्तावेज़ में शेल द्वारा दिया गया पाठ है । यह sed 'script' filenameया के समान काम करेगा process that writes to stdout | sed 'script'। क्या उससे मदद हुई?
mikeserv

हाँ, यह धन्यवाद! Dहर संशोधित लाइन के बिना डबल क्यों है? (आपने इसका उपयोग आवश्यक के रूप में किया है; शायद मैं sedबहुत अच्छी तरह से नहीं जानता हूं )
बोउपार्क

1
@BowPark - जब आप छोड़ते हैं तो आपको डबल्स मिलते हैं Dक्योंकि Dअन्यथा Dआउटपुट से eletes जो आपको अब दोगुना दिखाई देता है। मैंने अभी-अभी एक संपादन किया है और मैं जल्द ही इस पर विस्तार कर सकता हूं।
मोकेसर

1
@BowPark - ठीक है, मैंने इसे अपडेट किया है और विकल्प प्रदान किए हैं। अब पढ़ना / समझना थोड़ा आसान हो सकता है। मैंने भी स्पष्ट रूप से Dबात को संबोधित किया ।
मोकेसर

7

ठीक है, मैं कुछ सरल तरीकों के बारे में सोच सकता हूं, लेकिन न तो इसमें शामिल है grep(जो वैसे भी प्रतिस्थापन नहीं करता है) या sed

  1. पर्ल

    बदलने के लिए प्रत्येक की घटना "line"\n<second>के साथ other characters, उपयोग करें:

    $ perl -00pe 's/"line"\n<second>/other characters /g' file
    first other characters line and so on
    

    या, "line"\n<second>एक के रूप में कई, लगातार घटनाओं का इलाज करने के लिए , और उन सभी को एक एकल के साथ बदलें other characters, उपयोग करें:

    perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
    

    उदाहरण:

    $ cat file
    first "line"
    <second>"line"
    <second>"line"
    <second>line and so on
    $ perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
    first other characters line and so on
    

    -00पर्ल को फ़ाइल को "पैराग्राफ मोड" में पढ़ने का कारण है, जिसका अर्थ है कि "लाइनों" को \n\nइसके बजाय परिभाषित किया गया है \n, अनिवार्य रूप से, प्रत्येक पैराग्राफ को एक लाइन के रूप में माना जाता है। इसलिए प्रतिस्थापन एक नई रेखा के पार मेल खाता है।

  2. awk

    $  awk -v RS="\n\n" -v ORS="" '{
          sub(/"line"\n<second>/,"other characters ", $0)
          print;
        }' file 
    first other characters line and so on
    

    एक ही मूल विचार, हम पूरी फ़ाइल को स्लूर RSकरने के \n\nलिए रिकॉर्ड सेपरेटर ( ) सेट करते हैं , फिर आउटपुट रिकॉर्ड सेपरेटर को कुछ भी नहीं (अन्यथा एक अतिरिक्त न्यूलाइन मुद्रित होता है) और फिर sub()रिप्लेसमेंट बनाने के लिए फ़ंक्शन का उपयोग करें ।


2
@mikeserv? कौनसा? दूसरा माना जाता है, ओपी ने कहा कि वे चाहते हैं कि "एक या एक से अधिक घटनाओं को प्रतिस्थापित किया जाए", इसलिए पैराग्राफ को अच्छी तरह से खाने से वे उम्मीद कर सकते हैं।
terdon

बहुत अच्छी बात है। मुझे लगता है कि मैंने प्रत्येक बार अधिक ध्यान केंद्रित किया और प्राप्त किया , लेकिन मुझे लगता है कि यह स्पष्ट नहीं है कि क्या घटना के प्रति एक प्रतिस्थापन या घटनाओं के अनुक्रम के प्रति एक प्रतिस्थापन होना चाहिए ... @BowPark?
1:15 बजे mikeserv

यह प्रति घटना एक प्रतिस्थापन की आवश्यकता है।
बोवपार्क

@ BowPark ठीक है, तो पहले पर्ल दृष्टिकोण या awk दोनों काम करना चाहिए। क्या वे आपको वांछित आउटपुट नहीं देते हैं?
terdon

यह काम करता है, धन्यवाद, लेकिन तीसरी पंक्ति के साथ awkहोना चाहिए print;}' file। मुझे पर्ल से बचने और अधिमानतः उपयोग करने की आवश्यकता है sed, वैसे भी आपने अच्छे विकल्प सुझाए हैं।
बोवपार्क

6

पूरी फ़ाइल पढ़ें और एक वैश्विक प्रतिस्थापन करें:

sed -n 'H; ${x; s/"line"\n<second>/other characters /g; p}' <<END
first "line"
<second> line followed by "line"
<second> and last
END
first other characters  line followed by other characters  and last

हाँ। यह काम करता है, लेकिन क्या होगा अगर मेरे पास कई घटनाएं हैं?
बोवपार्क

हुंह, सही है। फिक्स्ड
ग्लेन जैकमैन

1
फिर से नाइटपिक के लिए खेद है, लेकिन ${cmds}जीएनयू-विशिष्ट है - सबसे अन्य sedएस को एक \nईलाइन या -eबीच में pऔर ब्रेक की आवश्यकता होगी }। आप कोष्ठक से पूरी तरह से बच सकते हैं - और \nआंशिक रूप से - और यहां तक कि पहली पंक्ति पर एक अतिरिक्त ईलाइन वर्ण डालने से बचें जैसे:sed 'H;1h;$!d;x;s/"line"\n<second>/other characters /g'
mikeserv

मैंने इसका परीक्षण किया और यह पोर्टेबल नहीं है। यह आउटपुट की शुरुआत में एक अतिरिक्त नई-लाइन प्रिंट करता है, लेकिन परिणाम GNU पर सही है।
बोवपार्क

अग्रणी न्यूलाइन को हटाने के लिए: sed -n '1{h;n};H; ${x; s/"line"\n<second>/other characters /g; p}'- हालाँकि यह अप्राप्य है।
ग्लेन जैकमैन

3

यहाँ ग्लेन के उत्तर पर एक संस्करण दिया गया है जो आपके कई लगातार होने पर काम करेगा ( sedकेवल GNU के साथ काम करता है ):

sed ':x /"line"/N;s/"line"\n<second>/other characters/;/"line"/bx' your_file

:xशाखाओं के लिए सिर्फ एक लेबल है। मूल रूप से, यह क्या करता है, यह प्रतिस्थापन के बाद लाइन की जांच करता है और अगर यह अभी भी मेल खाता है "line", तो यह :xलेबल पर वापस शाखा bxकरता है (यही करता है) और बफर में एक और लाइन जोड़ता है और इसे संसाधित करना शुरू कर देता है।


@mikeserv आप क्या मतलब के बारे में विशिष्ट हो। इसने मेरे लिए काम किया।
जोसेफ आर।

@ माइकर्स आई एम सॉरी, मैं वास्तव में नहीं जानता कि आप किस बारे में बात कर रहे हैं। मैंने उपरोक्त कोड लाइन को अपने टर्मिनल में वापस कॉपी किया और यह सही तरीके से काम किया।
जोसेफ आर।

1
हटा दिया गया - यह जाहिरा तौर पर GNU में काम करता है sedजो अपने गैर-POSIX लेबल को संभालने के लिए काफी दूर तक ले जाता है ताकि लेबल घोषणा के लिए एक सीमांकक के रूप में एक स्थान को स्वीकार किया जा सके। हालांकि, आपको ध्यान देना चाहिए कि कोई भी अन्य sedविफल हो जाएगा - और इसके लिए असफल हो जाएगा N। अंतिम पंक्ति पर sedछोड़ने से पहले पैटर्न-स्पेस को प्रिंट करने के लिए GNU POSIX दिशानिर्देशों को तोड़ता है N, लेकिन POSIX यह स्पष्ट करता है कि यदि Nअंतिम पंक्ति में एक कमांड को पढ़ा जाता है तो कुछ भी प्रिंट नहीं किया जाना चाहिए।
mikeserv

यदि आप GNU को निर्दिष्ट करने के लिए पोस्ट को संपादित करते हैं तो मैं अपने वोट को उलट दूंगा और इन टिप्पणियों को हटा दूंगा। इसके अलावा, यह GNU के vकमांड के बारे में सीखने लायक हो सकता है जो हर दूसरे में टूटता है sedलेकिन GNU संस्करण 4 और उससे अधिक में एक नो-ऑप है।
mikeserv

1
उस मामले में मैं की पेशकश करेगा एक और - इस portably की तरह किया जा सकता है: sed -e :x -e '/"line"/{$!N' -e '};s/"line"\n<second>/other characters/;/"line"/bx'
mikeserv
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.