SED रेगेक्स के साथ गैर-लालची मैच (अनुकरण पर्ल का? *?)


22

मैं (समावेशी) sedकी पहली ABऔर पहली घटना के बीच एक स्ट्रिंग में कुछ भी बदलने के लिए उपयोग करना चाहता हूं ।ACXXX

उदाहरण के लिए , मेरे पास यह स्ट्रिंग है (यह स्ट्रिंग केवल एक परीक्षण के लिए है):

ssABteAstACABnnACss

और मैं इसके समान आउटपुट देना चाहूंगा ssXXXABnnACss:।


मैंने इसके साथ किया perl:

$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss

लेकिन मैं इसे लागू करना चाहता हूं sed। निम्नलिखित (पर्ल-संगत रेगेक्स का उपयोग करके) काम नहीं करता है:

$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss

2
इसका कोई मतलब नहीं है। आपके पास पर्ल में एक कार्यशील समाधान है, लेकिन आप Sed का उपयोग करना चाहते हैं, क्यों?
Kusalananda

जवाबों:


16

सेड रेगेक्स सबसे लंबे मैच से मेल खाता है। सेड के पास गैर-लालची के बराबर नहीं है।

जाहिर है कि हम जो करना चाहते हैं वह मैच है

  1. AB,
    उसके बाद
  2. कुछ भी की किसी भी राशि के अलावा अन्य AC,
    द्वारा पीछा किया
  3. AC

दुर्भाग्य से, sed# 2 नहीं कर सकते - कम से कम एक बहु-चरित्र नियमित अभिव्यक्ति के लिए नहीं। बेशक, एकल-चरित्र नियमित अभिव्यक्ति जैसे @(या यहां तक ​​कि [123]) के लिए, हम कर सकते हैं [^@]*या [^123]*। और इसलिए हम की सभी घटनाओं को बदलने के द्वारा sed की सीमाओं के आसपास काम कर सकते हैं ACकरने के लिए @और उसके बाद के लिए खोज

  1. AB,
    उसके बाद
  2. के अलावा और कुछ के किसी भी संख्या @,
    द्वारा पीछा किया
  3. @

इस तरह:

sed 's/AC/@/g; s/AB[^@]*@/XXX/; s/@/AC/g'

पिछला भाग @वापस करने के बेजोड़ उदाहरणों को बदलता है AC

लेकिन, ज़ाहिर है, यह एक लापरवाह दृष्टिकोण है, क्योंकि इनपुट में पहले से ही @अक्षर शामिल हो सकते हैं , इसलिए, उन्हें मिलान करके, हम गलत सूचनाएं प्राप्त कर सकते हैं। हालाँकि, चूंकि शेल के किसी भी वेरिएबल में कभी भी NUL ( \x00) वर्ण नहीं होगा, NUL संभवतः इसके बजाय उपरोक्त कार्य में उपयोग करने के लिए एक अच्छा चरित्र है @:

$ echo 'ssABteAstACABnnACss' | sed 's/AC/\x00/g; s/AB[^\x00]*\x00/XXX/; s/\x00/AC/g'
ssXXXABnnACss

एनयूएल के उपयोग के लिए जीएनयू सेड की आवश्यकता होती है। (यह सुनिश्चित करने के लिए कि GNU सुविधाएँ सक्षम हैं, उपयोगकर्ता के पास शेल चर POSIXLY_CORRECT सेट नहीं होना चाहिए)

यदि आप -zएनयूएल-अलग इनपुट को संभालने के लिए GNU के झंडे के साथ sed का उपयोग कर रहे हैं , जैसे कि आउटपुट find ... -print0, तो NUL पैटर्न स्पेस में नहीं होगा और NUL यहां प्रतिस्थापन के लिए एक अच्छा विकल्प है।

हालाँकि NUL एक bash वैरिएबल में नहीं हो सकता है, लेकिन इसे printfकमांड में शामिल करना संभव है । यदि आपके इनपुट स्ट्रिंग में NUL सहित कोई भी वर्ण हो सकता है, तो स्टीफन चेज़लस का उत्तर देखें जो एक चतुर एस्कॉर्ट विधि जोड़ता है।


मैंने सिर्फ एक लंबा स्पष्टीकरण जोड़ने के लिए आपके उत्तर को संपादित किया; बेझिझक इसे ट्रिम करें या इसे वापस रोल करें।
जी-मैन का कहना है कि 'मोनिका'

@ जी-मैन यह एक उत्कृष्ट व्याख्या है! बहुत अच्छी तरह से किया। धन्यवाद।
जॉन १०२४

आप कर सकते हैं echoया printfएक `\ 000 'ठीक बैश में (या इनपुट एक फ़ाइल से आ सकता है)। लेकिन सामान्य तौर पर, पाठ की एक स्ट्रिंग निश्चित रूप से NULs नहीं है।
ilkachachu

@ilkkachu आप इसके बारे में सही हैं। मुझे जो लिखा जाना चाहिए वह यह है कि कोई शेल चर या पैरामीटर में एनयूएल नहीं हो सकता है। उत्तर अपडेट किया गया।
John1024

यदि आप फिर ACसे AC@और वापस बदल गए तो क्या यह बहुत अधिक सुरक्षित नहीं होगा ?
माइकल व्हीकल्स

7

कुछ sedकार्यान्वयन के लिए समर्थन है। ssedPCRE मोड है:

ssed -R 's/AB.*?AC/XXX/g'

AT & T ast sed में संवर्धित रीजैक्स का उपयोग करते समय संयोजन और निषेध होता है :

sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/g'

आंशिक रूप से, आप इस तकनीक का उपयोग कर सकते हैं: अंत स्ट्रिंग (यहां AC) को एक एकल वर्ण के साथ बदलें जो शुरुआत या अंत स्ट्रिंग (जैसे :यहां) में नहीं होता है s/AB[^:]*://, इसलिए आप कर सकते हैं , और उस चरित्र में इनपुट में प्रकट हो सकता है , एक भागने तंत्र का उपयोग करें जो शुरुआत और अंत के तारों से टकराता नहीं है।

एक उदाहरण:

sed 's/_/_u/g; # use _ as the escape character, escape it
     s/:/_c/g; # escape our replacement character
     s/AC/:/g; # replace the end string
     s/AB[^:]*:/XXX/g; # actual replacement
     s/:/AC/g; # restore the remaining end strings
     s/_c/:/g; # revert escaping
     s/_u/_/g'

जीएनयू के साथ sed, एक दृष्टिकोण प्रतिस्थापन के रूप में न्यूलाइन का उपयोग करना है। क्योंकि sedएक समय में एक पंक्ति की प्रक्रिया होती है, पैटर्न स्पेस में न्यूलाइन कभी नहीं होती है, इसलिए कोई भी ऐसा कर सकता है:

sed 's/AC/\n/g;s/AB[^\n]*\n/XXX/g;s/\n/AC/g'

वे आम तौर पर अन्य sedकार्यान्वयन के साथ काम नहीं करते हैं क्योंकि वे समर्थन नहीं करते हैं [^\n]। GNU के साथ sedआपको यह सुनिश्चित करना होगा कि POSIX संगतता सक्षम नहीं है (जैसे POSIXLY_CORRECT पर्यावरण चर के साथ)।


6

नहीं, sed regexes में गैर-लालची मिलान नहीं है।

आप की पहली आवृत्ति के लिए सभी पाठ मिलान कर सकते AC"युक्त नहीं कुछ भी का उपयोग करके ACके बाद" ACजो पर्ल के रूप में एक ही है, .*?AC। बात यह है कि, "कुछ भी शामिल नहीं है AC" को एक नियमित अभिव्यक्ति के रूप में आसानी से व्यक्त नहीं किया जा सकता है: हमेशा एक नियमित अभिव्यक्ति होती है जो एक नियमित अभिव्यक्ति के निषेध को पहचानती है, लेकिन नकारात्मक regex तेजी से जटिल हो जाता है। और पोर्टेबल सेड में, यह बिल्कुल भी संभव नहीं है, क्योंकि नकारात्मक रेगेक्स को एक ऐसे विकल्प को समूहीकृत करने की आवश्यकता होती है, जो विस्तारित नियमित अभिव्यक्तियों (जैसे कि awk) में मौजूद हो, लेकिन पोर्टेबल बुनियादी नियमित अभिव्यक्तियों में नहीं। सीएनयू के कुछ संस्करण, जैसे कि GNU sed, में BRE के एक्सटेंशन होते हैं जो इसे सभी संभव नियमित अभिव्यक्तियों को व्यक्त करने में सक्षम बनाते हैं।

sed 's/AB\([^A]*\|A[^C]\)*A*AC/XXX/'

रेगेक्स को नकारने की कठिनाई के कारण, यह अच्छी तरह से सामान्य नहीं होता है। इसके बजाय आप क्या कर सकते हैं अस्थायी रूप से लाइन को बदलना है। कुछ sed कार्यान्वयन में, आप एक मार्कर के रूप में newlines का उपयोग कर सकते हैं, क्योंकि वे एक इनपुट लाइन में नहीं दिखाई दे सकते हैं (और यदि आपको कई मार्करों की आवश्यकता है, तो एक अलग चरित्र के बाद newline का उपयोग करें)।

sed -e 's/AC/\
&/g' -e 's/AB[^\
]*\nAC/XXX/' -e 's/\n//g'

हालाँकि, सावधान रहें कि बैकस्लैश-न्यूलाइन कुछ सेडान संस्करणों के साथ सेट कैरेक्टर में काम नहीं करता है। विशेष रूप से, यह GNU sed में काम नहीं करता है, जो कि गैर-एम्बेडेड लिनक्स पर sed कार्यान्वयन है; GNU sed में आप \nइसके बजाय उपयोग कर सकते हैं :

sed -e 's/AC/\
&/g' -e 's/AB[^\n]*\nAC/XXX/' -e 's/\n//g'

इस विशिष्ट मामले में, पहली ACको एक नई पंक्ति से बदलना पर्याप्त है । मैंने जो दृष्टिकोण ऊपर प्रस्तुत किया है वह अधिक सामान्य है।

Sed में एक अधिक शक्तिशाली दृष्टिकोण लाइन को होल्ड स्पेस में सहेजना है, लाइन के सभी "दिलचस्प" भाग को हटा दें, होल्ड स्पेस और पैटर्न स्पेस का आदान-प्रदान करें या पैटर्न स्पेस को होल्ड स्पेस में रखें और दोहराएं। हालांकि, यदि आप ऐसी चीजें करना शुरू करते हैं जो इस जटिल हैं, तो आपको वास्तव में जागरण पर स्विच करने के बारे में सोचना चाहिए। अवीक में गैर-लालची मिलान भी नहीं है, लेकिन आप एक स्ट्रिंग को विभाजित कर सकते हैं और भागों को चर में बचा सकते हैं।


@ilkkachu नहीं, यह नहीं है। s/\n//gसभी नए सिरे से हटाता है।
गिल्स एसओ- बुराई को रोकना '

asdf। सही है, मेरा बुरा।
20k पर ilkachachu

3

sed - गैर लालची क्रिस्टोफ सिघर्ट द्वारा मिलान

गैर लालची सेड में मिलान करने की चाल मैच को समाप्त करने वाले को छोड़कर सभी वर्णों का मिलान करना है। मुझे पता है, एक नो-ब्रेनर, लेकिन मैंने उस पर कीमती मिनट बर्बाद कर दिए और शेल स्क्रिप्ट, सब के बाद, त्वरित और आसान होनी चाहिए। तो मामले में किसी और को इसकी आवश्यकता हो सकती है:

लालची मिलान

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

गैर लालची मिलान

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar


3
"नो-ब्रेनर" शब्द अस्पष्ट है। इस मामले में, यह स्पष्ट नहीं है कि आप (या क्रिस्टोफ़ सिगरहट) ने इसके माध्यम से सोचा था। विशेष रूप से, यह अच्छा होता अगर आपने दिखाया होता कि प्रश्न में विशिष्ट समस्या को कैसे हल किया जाए (जहाँ एक से अधिक वर्णों का शून्य से अधिक-अभिव्यक्ति का अनुसरण होता है ) । आप पा सकते हैं कि यह उत्तर उस मामले में अच्छी तरह से काम नहीं करता है।
स्कॉट

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

0

आपके मामले में आप इस तरह से चार को बंद करने की उपेक्षा कर सकते हैं:

echo 'ssABteAstACABnnACss' | sed 's/AB[^C]*AC/XXX/'

2
सवाल कहते हैं, "मैं पहली बार के बीच कुछ भी बदलने के लिए चाहते हैं ABऔर की पहली आवृत्ति ACके साथ XXX...," और देता है ssABteAstACABnnACssएक के रूप में उदाहरण के इनपुट। यह उत्तर उस उदाहरण के लिए काम करता है , लेकिन सामान्य रूप से इस सवाल का जवाब नहीं देता है। उदाहरण के लिए, ssABteCstACABnnACssआउटपुट भी प्राप्त करना चाहिए aaXXXABnnACss, लेकिन आपकी कमांड अपरिवर्तित होकर इस रेखा को पार करती है।
जी-मैन का कहना है कि 'मोनिका'

0

समाधान काफी सरल है। .*लालची है, लेकिन यह बिल्कुल लालची नहीं है। ssABteAstACABnnACssरेगेक्स के खिलाफ मिलान पर विचार करें AB.*AC। इसके ACबाद .*वास्तव में एक मैच होना चाहिए। समस्या यह है कि क्योंकि .*लालची है, बाद वाला पहले वाले के बजाय आखिरीAC मैच करेगा । पहली खाती है, जबकि शाब्दिक regexp में ssABteAstACABnn में पिछले एक से मेल खाता है एसी एस एस। ऐसा होने से रोकने के लिए, बस पहले को किसी हास्यास्पद चीज़ से बदलें , दूसरे से अलग करने के लिए और किसी चीज़ से। AC.*ACACAC

echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss

लालची .*अब के पैर में बंद हो जाएगा -foobar-में ssABteAst-foobar-ABnnACss, क्योंकि वहाँ कोई अन्य है -foobar-इस से -foobar-, और regexp -foobar- चाहिए एक मैच है। पिछली समस्या यह थी कि regexp के ACदो मैच थे, लेकिन क्योंकि .*लालची था, के लिए अंतिम मैच ACका चयन किया गया था। हालाँकि, -foobar-केवल एक मैच ही संभव है, और यह मैच साबित करता है कि .*यह बिल्कुल लालची नहीं है। निम्नलिखित के लिए बस स्टॉप .*तब होता है जब बाकी रेगेक्सप के लिए केवल एक मैच रहता है .*

ध्यान दें कि यह समाधान विफल हो जाएगा यदि ACपहली बार दिखाई देता है ABक्योंकि गलत ACको बदल दिया जाएगा -foobar-। उदाहरण के लिए, पहले sedप्रतिस्थापन के बाद , ACssABteAstACABnnACssबन जाता है -foobar-ssABteAstACABnnACss; इसलिए, एक मैच के खिलाफ नहीं पाया जा सकता है AB.*-foobar-। हालांकि, यदि अनुक्रम हमेशा होता है ... एबी ... एसी ... एबी ... एसी ..., तो यह समाधान सफल होगा।


0

एक वैकल्पिक स्ट्रिंग को बदलने के लिए ताकि आप है चाहते हैं लालची मैच

echo "ssABtCeCAstACABnnACss" | rev | sed -E "s/(.*)CA.*BA(.*)/\1CA+-+-+-+-BA\2/" | rev

revस्ट्रिंग को उल्टा करने के लिए, अपने मैच के मापदंड को उल्टा करने के लिए उपयोग करें , sedसामान्य फैशन में उपयोग करें और फिर परिणाम को उल्टा करें…।

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