मैं k
एक शब्द के केवल पहले उदाहरणों को बदलना चाहता हूं ।
मैं यह कैसे कर सकता हूँ?
उदाहरण के लिए। मान लें कि फ़ाइल foo.txt
में 'linux' शब्द के 100 उदाहरण हैं।
मुझे केवल पहले 50 घटनाओं को बदलना होगा।
मैं k
एक शब्द के केवल पहले उदाहरणों को बदलना चाहता हूं ।
मैं यह कैसे कर सकता हूँ?
उदाहरण के लिए। मान लें कि फ़ाइल foo.txt
में 'linux' शब्द के 100 उदाहरण हैं।
मुझे केवल पहले 50 घटनाओं को बदलना होगा।
जवाबों:
पहले खंड में sed
एक पंक्ति पर पहले k- आवृत्तियों को बदलने का उपयोग करने का वर्णन है । दूसरा खंड इस दृष्टिकोण को बढ़ाता है कि किसी फ़ाइल में केवल पहले k- आवृत्तियों को बदलने के लिए, भले ही वे किस लाइन पर दिखाई दें।
मानक सेड के साथ, एक लाइन पर किसी शब्द की k- वीं घटना को बदलने के लिए एक कमांड है। यदि k
3 है, उदाहरण के लिए:
sed 's/old/new/3'
या, सभी घटनाओं को बदल सकता है:
sed 's/old/new/g'
इनमें से न तो आप चाहते हैं।
GNU sed
एक विस्तार प्रदान करता है जो k-th घटना को और उसके बाद सभी को बदल देगा। यदि k 3 है, उदाहरण के लिए:
sed 's/old/new/g3'
ये आप क्या चाहते हैं करने के लिए जोड़ा जा सकता है। पहली 3 घटनाओं को बदलने के लिए:
$ echo old old old old old | sed -E 's/\<old\>/\n/g4; s/\<old\>/new/g; s/\n/old/g'
new new new old old
जहां \n
क्योंकि हम यकीन है कि यह कभी नहीं एक लाइन पर होता है हो सकता है यहाँ उपयोगी है।
हम तीन sed
प्रतिस्थापन आदेशों का उपयोग करते हैं :
s/\<old\>/\n/g4
यह जीएनयू विस्तार चौथे और के बाद के सभी घटनाओं को बदलने के लिए old
के साथ \n
।
विस्तारित रेगेक्स सुविधा \<
का उपयोग किसी शब्द की शुरुआत और शब्द \>
के अंत से मेल खाने के लिए किया जाता है। यह आश्वासन देता है कि केवल पूर्ण शब्दों का मिलान किया जाता है। विस्तारित रेगेक्स के लिए -E
विकल्प की आवश्यकता होती है sed
।
s/\<old\>/new/g
केवल पहली तीन घटनाएं old
बनी हुई हैं और यह उन सभी को बदल देता है new
।
s/\n/old/g
चौथी और शेष सभी घटनाओं को पहले चरण में old
बदल दिया गया \n
। यह उन्हें वापस उनकी मूल स्थिति में लौटाता है।
यदि जीएनयू sed उपलब्ध नहीं है और आप के पहले 3 घटनाओं को बदलना चाहते हैं old
के लिए new
है, तो तीन का उपयोग s
कमांड:
$ echo old old old old old | sed -E -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
new new new old old
यह अच्छी तरह से काम करता है जब k
एक छोटी संख्या होती है लेकिन बड़े पैमाने पर खराब होती है k
।
चूंकि कुछ गैर-जीएनयू सेड, अर्धविराम के साथ कमांड के संयोजन का समर्थन नहीं करते हैं, इसलिए यहां प्रत्येक कमांड को अपने स्वयं के -e
विकल्प के साथ पेश किया गया है । यह सत्यापित करने के लिए भी आवश्यक हो सकता है कि आपका sed
शब्द सीमा प्रतीकों का समर्थन करता है, \<
और \>
।
हम पूरी फाइल को पढ़ने के लिए sed बता सकते हैं और फिर प्रतिस्थापन कर सकते हैं। उदाहरण के लिए, old
बीएसडी-स्टाइल सेड के उपयोग की पहली तीन घटनाओं को बदलने के लिए :
sed -E -e 'H;1h;$!d;x' -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/'
Sed कमांड H;1h;$!d;x
पूरी फाइल को पढ़ता है।
क्योंकि उपरोक्त किसी भी GNU एक्सटेंशन का उपयोग नहीं करता है, इसे BSD (OSX) sed पर काम करना चाहिए। ध्यान दें, सोचा, कि इस दृष्टिकोण के लिए sed
लंबी लाइनों को संभालना आवश्यक है । जीएनयू sed
ठीक होना चाहिए। गैर-जीएनयू संस्करण का उपयोग sed
करने वालों को लंबी लाइनों को संभालने की क्षमता का परीक्षण करना चाहिए।
जीएनयू सेड के साथ, हम आगे g
बताए गए ट्रिक का उपयोग कर सकते हैं , लेकिन पहले तीन घटनाओं को बदलने के लिए, के साथ \n
प्रतिस्थापित किया \x00
गया है:
sed -E -e 'H;1h;$!d;x; s/\<old\>/\x00/g4; s/\<old\>/new/g; s/\x00/old/g'
यह दृष्टिकोण k
बड़े होने के साथ-साथ बढ़ता है। हालांकि, यह \x00
माना जाता है कि यह आपके मूल स्ट्रिंग में नहीं है। चूंकि चरित्र \x00
को बैश स्ट्रिंग में रखना असंभव है , इसलिए यह आमतौर पर एक सुरक्षित धारणा है।
tr '\n' '|' < input_file | sed …
। लेकिन, निश्चित रूप से, जो पूरे इनपुट को एक पंक्ति में परिवर्तित करता है, और कुछ गैर-जीएनयू सेड्स मनमाने ढंग से लंबी लाइनों को संभाल नहीं सकते हैं। (2) आप कहते हैं, "... ऊपर, उद्धृत स्ट्रिंग '|'
को किसी भी वर्ण या वर्णों के स्ट्रिंग द्वारा प्रतिस्थापित किया जाना चाहिए, ..." लेकिन आप tr
किसी वर्ण को स्ट्रिंग (लंबाई> 1) से बदलने के लिए उपयोग नहीं कर सकते । (३) अपने अंतिम उदाहरण में, आप कहते हैं -e 's/\<old\>/new/' -e 's/\<old\>/w/' | tr '\000' '\n'\>/new
। यह एक टाइपो के लिए लगता है -e 's/\<old\>/new/' -e 's/\<old\>/new/' -e 's/\<old\>/new/' | tr '\000' '\n'
।
Awk कमांड का इस्तेमाल शब्द की पहली N घटनाओं को बदलने के लिए किया जा सकता है।
यदि शब्द एक पूर्ण मिलान है, तो आदेश केवल प्रतिस्थापित होंगे।
नीचे दिए गए उदाहरण में, मैं पहले की जगह कर रहा हूँ 27
की घटनाओं old
के साथnew
उप का उपयोग करना
awk '{for(i=1;i<=NF;i++){if(x<27&&$i=="old"){x++;sub("old","new",$i)}}}1' file
जब तक यह मेल खाता है
old
, तब तक यह कमांड प्रत्येक क्षेत्र से गुजरता है , यह जाँचता है कि काउंटर 27 से नीचे है, वेतन वृद्धि और लाइन पर पहला मैच स्थानापन्न। फिर अगले फ़ील्ड / लाइन पर जाता है और दोहराता है।
फ़ील्ड को मैन्युअल रूप से बदलना
awk '{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
कमांड के समान लेकिन इससे पहले ही इसमें एक मार्कर है कि यह किस क्षेत्र पर है
($i)
, यह केवल फ़ील्ड के मान को बदल देताold
हैnew
।
पहले एक जाँच करना
awk '/old/&&x<27{for(i=1;i<=NF;i++)if(x<27&&$i=="old"&&$i="new")x++}1' file
जाँच कर रहा है कि लाइन पुरानी है और काउंटर 27 से नीचे है,
SHOULD
एक छोटी गति बढ़ाने के लिए प्रदान करें क्योंकि यह लाइनों को संसाधित नहीं करेगा जब ये झूठे होते हैं।
परिणाम
उदाहरण के लिए
old bold old old old
old old nold old old
old old old gold old
old gold gold old old
old old old man old old
old old old old dog old
old old old old say old
old old old old blah old
सेवा मेरे
new bold new new new
new new nold new new
new new new gold new
new gold gold new new
new new new man new new
new new new new dog new
new new old old say old
old old old old blah old
कहते हैं कि आप एक स्ट्रिंग के केवल पहले तीन उदाहरणों को बदलना चाहते हैं ...
seq 11 100 311 |
sed -e 's/1/\
&/g' \ #s/match string/\nmatch string/globally
-e :t \ #define label t
-e '/\n/{ x' \ #newlines must match - exchange hold and pattern spaces
-e '/.\{3\}/!{' \ #if not 3 characters in hold space do
-e 's/$/./' \ #add a new char to hold space
-e x \ #exchange hold/pattern spaces again
-e 's/\n1/2/' \ #replace first occurring '\n1' string w/ '2' string
-e 'b t' \ #branch back to label t
-e '};x' \ #end match function; exchange hold/pattern spaces
-e '};s/\n//g' #end match function; remove all newline characters
ध्यान दें: उपरोक्त संभवतः अंतर्निहित टिप्पणियों के साथ काम नहीं करेगा
... या मेरे उदाहरण के मामले में, '1' का ...
22
211
211
311
वहां मैं दो उल्लेखनीय तकनीकों का उपयोग करता हूं। पहले स्थान 1
पर एक पंक्ति की प्रत्येक घटना को प्रतिस्थापित किया जाता है \n1
। इस तरह, जैसा कि मैं अगली बार पुनरावर्ती प्रतिस्थापन करता हूं, मुझे यकीन है कि दो बार घटना को प्रतिस्थापित नहीं किया जा सकता है यदि मेरे प्रतिस्थापन स्ट्रिंग में मेरी प्रतिस्थापित स्ट्रिंग है। उदाहरण के लिए, अगर मैं इसके he
साथ प्रतिस्थापित करता हूं, तब hey
भी काम करेगा।
मैं इसे पसंद करता हूं:
s/1/\
&/g
दूसरे, मैं h
प्रत्येक घटना के लिए एक चरित्र को पुराने स्थान में जोड़कर प्रतिस्थापन की गिनती कर रहा हूं । एक बार जब मैं तीन तक पहुँचता हूं तो कोई घटना नहीं होती है। यदि आप इसे अपने डेटा पर लागू करते हैं और अपनी \{3\}
इच्छानुसार कुल प्रतिस्थापन को बदल देते हैं और आपके द्वारा प्रतिस्थापित /\n1/
करने के लिए जो भी मतलब है उसके पते, आपको केवल अपनी इच्छानुसार प्रतिस्थापित करना चाहिए।
मैंने केवल -e
पठनीयता के लिए सभी सामान किया। POSIXly इसे इस तरह लिखा जा सकता है:
nl='
'; sed "s/1/\\$nl&/g;:t${nl}/\n/{x;/.\{3\}/!{${nl}s/$/./;x;s/\n1/2/;bt$nl};x$nl};s/\n//g"
और w / ग्नू sed
:
sed 's/1/\n&/g;:t;/\n/{x;/.\{3\}/!{s/$/./;x;s/\n1/2/;bt};x};s/\n//g'
यह भी याद रखें कि sed
यह लाइन-ओरिएंटेड है - यह पूरी फाइल में नहीं पढ़ता है और फिर इसे वापस लूप करने का प्रयास करता है जैसा कि अन्य संपादकों में अक्सर होता है। sed
सरल और कुशल है। उस ने कहा, यह अक्सर निम्नलिखित की तरह कुछ करने के लिए सुविधाजनक है:
यहां एक छोटा शेल फ़ंक्शन है जो इसे एक साधारण निष्पादित कमांड में बंडल करता है:
firstn() { sed "s/$2/\
&/g;:t
/\n/{x
/.\{$(($1))"',\}/!{
s/$/./; x; s/\n'"$2/$3"'/
b t
};x
};s/\n//g'; }
तो इसके साथ ही मैं यह कर सकता हूं:
seq 11 100 311 | firstn 7 1 5
...और पाओ...
55
555
255
311
... या ...
seq 10 1 25 | firstn 6 '\(.\)\([1-5]\)' '\15\2'
...लेना...
10
151
152
153
154
155
16
17
18
19
20
251
22
23
24
25
... या, अपने उदाहरण से मेल खाने के लिए (परिमाण के एक छोटे से क्रम पर) :
yes linux | head -n 10 | firstn 5 linux 'linux is an os kernel'
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux is an os kernel
linux
linux
linux
linux
linux
पर्ल में एक छोटा विकल्प:
perl -pe 'BEGIN{$n=3} 1 while s/old/new/ && ++$i < $n' your_file
`$ N $ के मूल्य को अपनी पसंद के अनुसार बदलें।
यह काम किस प्रकार करता है:
new
के लिए old
( s/old/new/
जब भी यह कर सकते हैं) और, यह चर वृद्धि कर देता है $i
( ++$i
)।1 while ...
) पर तब तक काम करता रहता है जब तक कि यह $n
कुल प्रतिस्थापन से कम नहीं हो जाता है और यह उस रेखा पर कम से कम एक प्रतिस्थापन बना सकता है।शेल लूप का उपयोग करें और ex
!
{ for i in {1..50}; do printf %s\\n '0/old/s//new/'; done; echo x;} | ex file.txt
हां, यह थोड़ा नासमझ है।
;)
नोट: यदि old
फ़ाइल में 50 से कम इंस्टेंस हैं, तो यह विफल हो सकता है । (मैंने इसे परीक्षण नहीं किया है।) यदि ऐसा है, तो यह फ़ाइल को अनमॉडिफाइड छोड़ देगा।
बेहतर अभी तक, विम का उपयोग करें।
vim file.txt
qqgg/old<CR>:s/old/new/<CR>q49@q
:x
स्पष्टीकरण:
q # Start recording macro
q # Into register q
gg # Go to start of file
/old<CR> # Go to first instance of 'old'
:s/old/new/<CR> # Change it to 'new'
q # Stop recording
49@q # Replay macro 49 times
:x # Save and exit
Https://stackoverflow.com/questions/148451/how-to-use-sed-sed-to-replace-only-the-first-occurrence-in-a में वर्णित आदेशों पर लूप करने का एक सरल, लेकिन बहुत तेज़ समाधान नहीं है। -file
for i in $(seq 50) ; do sed -i -e "0,/oldword/s//newword/" file.txt ; done
यह विशेष रूप से sed कमान शायद जीएनयू sed और अगर केवल के लिए काम करता newword का हिस्सा नहीं है oldword । गैर-जीएनयू सेड के लिए यहां देखें कि किसी फाइल में केवल पहला पैटर्न कैसे बदलें।
जीएनयू के साथ awk
आप शब्द विभाजक RS
को शब्द सीमाओं द्वारा सीमांकित किए जाने के लिए रिकॉर्ड विभाजक सेट कर सकते हैं। फिर यह शेष रिकॉर्ड के लिए मूल रिकॉर्ड विभाजक को बनाए रखते हुए पहले रिकॉर्ड के लिए प्रतिस्थापन शब्द को आउटपुट पर रिकॉर्ड विभाजक सेट करने का मामला हैk
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, NR <= limit? replacement: RT}' file
या
awk -vRS='\\ylinux\\y' -vreplacement=unix -vlimit=50 \
'{printf "%s%s", $0, limit--? replacement: RT}' file