केवल पहली घटना को सेड के साथ बदलना चाहते हैं


26

मूल फ़ाइल

claudio
antonio
claudio
michele

मैं "क्लॉडिया" के साथ "क्लॉडियो" की केवल पहली घटना को बदलना चाहता हूं, इसलिए फ़ाइल परिणाम

claudia
antonio
claudio
michele

मैंने कोशिश की है

sed -e '1,/claudio/s/claudio/claudia/' nomi

लेकिन एक वैश्विक प्रतिस्थापन का प्रदर्शन करें। क्यों?


यहाँ देखें linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… और भी info sed: ( 0,/REGEXP/0 की एक पंक्ति संख्या का उपयोग पते के विनिर्देशन में किया जा सकता है, 0,/REGEXP/ताकि sedपहले इनपुट लाइन में भी REGEXP से मेल खाने का प्रयास करें। दूसरे शब्दों में, अन्य शब्दों में 0,/REGEXP/) के समान 1,/REGEXP/है, सिवाय इसके कि अगर ADDR2 इनपुट 0 के पहले पंक्ति से मेल खाती, / regexp / प्रपत्र यह विचार करेंगे सीमा समाप्त करने के लिए है, जबकि 1, / regexp / प्रपत्र अपनी सीमा की शुरुआत से मेल खाते हैं और इसलिए सीमा अवधि कर देगा नियमित अभिव्यक्ति की दूसरी घटना तक)
जिमिम


awk '/claudio/ && !ok { sub(/claudio/,"claudia"); ok=1 } 1' nomiकरना चाहिए
एडम काटज़

जवाबों:


23

यदि आप GNU का उपयोग कर रहे हैं sed, तो प्रयास करें:

sed -e '0,/claudio/ s/claudio/claudia/' nomi

sedरेगेक्स के लिए जाँच शुरू नहीं करता है जो उस सीमा के बाद एक सीमा तक समाप्त होता है।

से man sed (POSIX मैनपेज, मेरा जोर):

दो पतों वाला एक संपादन आदेश समावेशी श्रेणी का चयन करेगा
पहले पैटर्न स्पेस के माध्यम से जो पहले पते से मेल खाता है  है
अगला पैटर्न स्थान जो दूसरे से मेल खाती है। 

का उपयोग करते हुए awk

में पर्वतमाला awkअधिक काम के रूप में आप उम्मीद कर रहे थे:

$ awk 'NR==1,/claudio/{sub(/claudio/, "claudia")} 1' nomi
claudia
antonio
claudio
michele

स्पष्टीकरण:

  • NR==1,/claudio/

    यह एक सीमा है जो लाइन 1 से शुरू होती है और पहली घटना के साथ समाप्त होती है claudio

  • sub(/claudio/, "claudia")

    जब हम सीमा में होते हैं, तो इस स्थानापन्न कमांड को निष्पादित किया जाता है।

  • 1

    यह awk की क्रिप्टि शॉर्टहैंड प्रिंट लाइन के लिए है।


1
sedहालांकि यह GNU मानता है ।
स्टीफन चेजलस

@ स्टीफनचेज़लस यह भी काम करता है अगर POSIXLY_CORRECT सेट है, लेकिन मुझे लगता है कि इसका उतना मतलब नहीं है जितना मैं चाहता हूं। उत्तर अपडेट किया गया (मुझे बीएसडी टेस्ट मशीनों की कमी है)।
जॉन १०२४

awk '!r && /claudio/ {sub(/claudio/,"claudia"); r=1} 1'
जाग

@glennjackman याawk !x{x=sub(/claudio/,"claudia")}1

मैं भी पहले भाग में एक अलग सीमांकक का सफलतापूर्वक उपयोग नहीं कर सका:0,/claudio/
पाट मायरोन

4

यहां sed के साथ 2 और प्रोग्रामेटिक प्रयास हैं: वे दोनों पूरी फ़ाइल को एक ही स्ट्रिंग में पढ़ते हैं, फिर खोज केवल पहले वाले को बदल देगी।

sed -n ':a;N;$bb;ba;:b;s/\(claudi\)o/\1a/;p' file
sed -n '1h;1!H;${g;s/\(claudi\)o/\1a/;p;}' file

कमेंट्री के साथ:

sed -n '                # don't implicitly print input
  :a                    # label "a"
  N                     # append next line to pattern space
  $bb                   # at the last line, goto "b"
  ba                    # goto "a"
  :b                    # label "b"
  s/\(claudi\)o/\1a/    # replace
  p                     # and print
' file
sed -n '                # don't implicitly print input
  1h                    # put line 1 in the hold space
  1!H                   # for subsequent lines, append to hold space
  ${                    # on the last line
    g                     # put the hold space in pattern space
    s/\(claudi\)o/\1a/    # replace
    p                     # print
  }
' file

3

GNU sedका एक नया संस्करण -zविकल्प का समर्थन करता है।

आम तौर पर, sed अंत-लाइन चरित्र (नई लाइन या कैरिज रिटर्न) तक वर्णों की एक स्ट्रिंग को पढ़कर एक पंक्ति पढ़ता है।
इसके बजाय "NULL" वर्ण का उपयोग करने के लिए sed के GNU संस्करण ने 4.2.2 संस्करण में एक सुविधा जोड़ी। यह उपयोगी हो सकता है यदि आपके पास ऐसी फाइलें हैं जो NULL को रिकॉर्ड विभाजक के रूप में उपयोग करती हैं। कुछ GNU उपयोगिताओं में एक नई लाइन के बजाय NULL का उपयोग करने वाला आउटपुट उत्पन्न किया जा सकता है, जैसे "find -print0" या "grep -lZ"।

जब आप sedविभिन्न लाइनों पर काम करना चाहते हैं तो आप इस विकल्प का उपयोग कर सकते हैं ।

echo 'claudio
antonio
claudio
michele' | sed -z 's/claudio/claudia/'

रिटर्न

claudia
antonio
claudio
michele

1

यदि आप awkपहले से ही प्रतिस्थापन किया गया था, तो यह जानने के लिए कि आप किसी ध्वज का उपयोग कर सकते हैं । यदि नहीं, तो आगे बढ़ें:

$ awk '!f && /claudio/ {$0="claudia"; f=1}1' file
claudia
antonio
claudio
michele

1

यह वास्तव में वास्तव में आसान है यदि आप बस थोड़ी देरी सेट करते हैं - अविश्वसनीय एक्सटेंशन के लिए पहुंचने की कोई आवश्यकता नहीं है:

sed '$H;x;1,/claudio/s/claudio/claudia/;1d' <<\IN
claudio
antonio
claudio
michele
IN

वह सिर्फ पहली पंक्ति को दूसरी और दूसरी को तीसरी और आदि से बचाता है।

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

claudia
antonio
claudio
michele

1

और एक और विकल्प

sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/claudio/claudia/;}" -- nomi

लाभ यह है कि दोहरे उद्धरण चिह्नों का उपयोग करता है, इसलिए आप चर का उपयोग अंदर कर सकते हैं, अर्थात।

export chngFrom=claudio
export chngTo=claudia
sed --in-place=*.bak -e "1 h;1! H;\$! d;$ {g;s/${chngFrom}/${chngTo}/;}" -- nomi

1
हां आप ठीक हैं। सामान्य विचार समान है। लेकिन, कृपया, एकल को सीधे दोहरे उद्धरणों में स्थानापन्न करने का प्रयास करें, और देखें कि क्या यह काम करता है। शैतान विवरण में निहित है। इस उदाहरण में ये रिक्त स्थान हैं और एक बच जाता है। मेरा मानना ​​है कि पहले के उत्तरों की निरंतरता किसी के समय को बचा सकती है। और यही कारण है कि मैंने पोस्ट प्रकाशित करने का फैसला किया।
utom

1

यह भी पकड़ स्थान के बिना और पैटर्न अंतरिक्ष में सभी लाइनों को छुपाने के बिना किया जा सकता है:

sed -n '/claudio/{s/o/a/;bx};p;b;:x;p;n;bx' nomi

स्पष्टीकरण: हम "क्लैडियो" खोजने की कोशिश करते हैं और अगर हम इसे करते हैं तो हम छोटे प्रिंट-लोड-लूप के बीच :xऔर में कूदते हैं bx। अन्यथा हम अगली पंक्ति के साथ स्क्रिप्ट को प्रिंट और पुनः आरंभ करते हैं।

sed -n '      # do not print lines by default
  /claudio/ { # on lines that match "claudio" do ...
    s/o/a/    # replace "o" with "a"
    bx        # goto label x
  }           # end of do block
  p           # print the pattern space
  b           # go to the end of the script, continue with next line
  :x          # the label x for goto commands
  p           # print the pattern space
  n           # load the next line in the pattern space (clearing old contents)
  bx          # goto the label x
  ' nomi

1
sed -n '/claudia/{p;Q}'

sed -n '           # don't print input
    /claudia/      # regex search
    {              # when match is found do
    p;             # print line
    Q              # quit sed, don't print last buffered line
    {              # end do block

1
क्या आपने प्रश्न पढ़ने से परेशान हैं?
डॉन_क्रिस्टी

1

Sumary

GNU सिंटैक्स:

sed '/claudio/{s//claudia/;:p;n;bp}' file

या यहां तक ​​कि (केवल एक बार उपयोग किए जाने वाले शब्द को बदलने के लिए:

sed '/\(claudi\)o/{s//\1a/;:p;n;bp}' file

या, POSIX सिंटैक्स में:

sed -e '/claudio/{s//claudia/;:p' -e 'n;bp' -e '}' file

किसी भी तलछट पर काम करता है, पहले खोजने के लिए जितनी लाइनें चाहिए उतनी ही प्रक्रिया claudioकरता है, भले ही काम करता होclaudio पहली पंक्ति में है और के रूप में यह केवल एक regex स्ट्रिंग का उपयोग कम है।

विस्तार

केवल एक पंक्ति को बदलने के लिए आपको केवल एक पंक्ति का चयन करना होगा

1,/claudio/(आपके प्रश्न से) चयन का उपयोग करना :

  • पहली पंक्ति से (बिना शर्त)
  • करने के लिए अगले लाइन है कि स्ट्रिंग है claudio
$ cat file
claudio 1
antonio 2
claudio 3
michele 4

$ sed -n '1,/claudio/{p}' file
claudio 1
antonio 2
claudio 3

किसी भी पंक्ति का चयन करने के लिए claudio, जिसमें उपयोग करें:

$ sed -n `/claudio/{p}` file
claudio 1
claudio 3

और फ़ाइल में केवल पहले का चयन करने के लिए claudio, उपयोग करें:

sed -n '/claudio/{p;q}' file
claudio 1

फिर, आप उस लाइन पर केवल एक प्रतिस्थापन कर सकते हैं:

sed '/claudio/{s/claudio/claudia/;q}' file
claudia 1

जो लाइन पर रेगेक्स मैच की केवल पहली घटना को बदल देगा , भले ही पहले पर एक से अधिक हो, लाइन ।

बेशक, /claudio/रेगेक्स को सरल बनाया जा सकता है:

$ sed '/claudio/{s//claudia/;q}' file
claudia 1

और, तब, केवल एक चीज गायब है अन्य सभी लाइनों को संयुक्त राष्ट्र में संशोधित करने के लिए:

sed '/claudio/{s//claudia/;:p;n;bp}' file
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.