LinuxQuestions.org
Visit the LQ Articles and Editorials section
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 04-05-2010, 02:56 PM   #1
kushalkoolwal
Senior Member
 
Registered: Feb 2004
Location: Middle of nowhere
Distribution: Debian Squeeze
Posts: 1,249

Rep: Reputation: 48
Replace 2nd occurrence of a string in a file - sed or awk?


So I know how to replace a particular instance (say 3rd one) of a word in a line using sed based on the sed one-liners. However I would like to replace a particular instance of a word in the entire file. I tried searching on the Internet but did not find anything useful.

For example, here is a file:
Code:
John
Betty
Jack
Ron
Jack
Paul
So now I would like to replace the second instance of Jack (in red color) with "Rob" (for example). Not quite sure how to do that? I tried couple of things from here but they did not work.

Thanks

Last edited by kushalkoolwal; 04-05-2010 at 04:23 PM.
 
Old 04-05-2010, 03:42 PM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950
Try this.

Code:
sed  '0,/Jack/! s/Jack/Rob/' file.txt
The exclamation mark negates everything from the beginning of the file to the first "Jack", so that the substitution operates on all the following lines. Note that I believe this is a gnu sed operation only.

If you need to operate on only the second occurrence, and ignore any subsequent matches, you can use a nested expression.
Code:
sed  '0,/Jack/! {0,/Jack/ s/Jack/Rob/}' file.txt
Here, the bracketed expression will operate on the output of the first part, but in this case, it will exit after changing the first matching "Jack".

PS, I've found the sed faq to be very helpful in cases like this.

Last edited by David the H.; 04-05-2010 at 05:35 PM. Reason: fixed mistake
 
Old 04-05-2010, 07:52 PM   #3
ghostdog74
Senior Member
 
Registered: Aug 2006
Posts: 2,697
Blog Entries: 5

Rep: Reputation: 241Reputation: 241Reputation: 241
use awk, where you can maintain a count of things. Its also easier to change if your future requirement is not the second instance.

Code:
awk '/Jack/{c++;if(c==2){sub("Jack","Rob");c=0}}1' file

Last edited by ghostdog74; 04-05-2010 at 07:55 PM.
 
1 members found this post helpful.
Old 04-06-2010, 09:00 PM   #4
Star_Gazer
Member
 
Registered: Aug 2009
Location: Virginia, United States
Distribution: openSUSE 11.2 KDE
Posts: 32

Rep: Reputation: 15
Interesting...

In the page of the document,
info:/sed/The "s" Command
which can be loaded in Konquerer.

Code:
The `s' command can be followed by zero or more of the following FLAGS: 
 `g'
      Apply the replacement to _all_ matches to the REGEXP, not just the
      first.


 `NUMBER'
      Only replace the NUMBERth match of the REGEXP.


      Note: the POSIX standard does not specify what should happen when
      you mix the `g' and NUMBER modifiers, and currently there is no
      widely agreed upon meaning across `sed' implementations.  For GNU
      `sed', the interaction is defined to be: ignore matches before the
      NUMBERth, and then match and replace all matches from the NUMBERth
      on.
Though I haven't quite got a grasp on it.
 
Old 04-07-2010, 07:36 AM   #5
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950Reputation: 1950
When you add a number after the substitution expression, the replacement will happen to the nth occurrence of the pattern on that line; just as 'g' will affect all occurrences on the same line. Combining the two makes it affect all matches from that position on.

Code:
testline="foo bar foo bar foo bar foo bar"

$ echo "$testline" | sed 's/foo/FOO/'
FOO bar foo bar foo bar foo bar

$ echo "$testline" | sed 's/foo/FOO/3'
foo bar foo bar FOO bar foo bar

$ echo "$testline" | sed 's/foo/FOO/g'
FOO bar FOO bar FOO bar FOO bar

$ echo "$testline" | sed 's/foo/FOO/3g'
foo bar foo bar FOO bar FOO bar
Sed works by copying a single line into it's pattern buffer, processing that line, then clearing it and moving on to the next line. This means that the s/// expression on its own cannot affect multiple lines. That's what the addressing expressions and the hold buffer are for.

Good catch on a poorly-known function though.

Last edited by David the H.; 04-07-2010 at 07:48 AM. Reason: Added some clarification
 
1 members found this post helpful.
Old 04-07-2010, 07:57 AM   #6
syg00
LQ Veteran
 
Registered: Aug 2003
Location: Australia
Distribution: Lots ...
Posts: 12,497

Rep: Reputation: 1077Reputation: 1077Reputation: 1077Reputation: 1077Reputation: 1077Reputation: 1077Reputation: 1077Reputation: 1077
ghostdog74, unless I'm misunderstanding (quite likely), won't that change every second occurrence, not just the second one ?.
 
Old 04-07-2010, 08:43 AM   #7
Star_Gazer
Member
 
Registered: Aug 2009
Location: Virginia, United States
Distribution: openSUSE 11.2 KDE
Posts: 32

Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
When you add a number after the substitution expression, the replacement will happen to the nth occurrence of the pattern on that line; just as 'g' will affect all occurrences on the same line. Combining the two makes it affect all matches from that position on.

Code:
testline="foo bar foo bar foo bar foo bar"

$ echo "$testline" | sed 's/foo/FOO/'
FOO bar foo bar foo bar foo bar

$ echo "$testline" | sed 's/foo/FOO/3'
foo bar foo bar FOO bar foo bar

$ echo "$testline" | sed 's/foo/FOO/g'
FOO bar FOO bar FOO bar FOO bar

$ echo "$testline" | sed 's/foo/FOO/3g'
foo bar foo bar FOO bar FOO bar
Sed works by copying a single line into it's pattern buffer, processing that line, then clearing it and moving on to the next line. This means that the s/// expression on its own cannot affect multiple lines. That's what the addressing expressions and the hold buffer are for.

Good catch on a poorly-known function though.
Ah, thanks for pointing that out

It was driving me batty when I kept trying to test it, thus, I gave up.

I'll note this for future references.
 
Old 04-07-2010, 10:45 AM   #8
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,698

Rep: Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988Reputation: 1988
Quote:
ghostdog74, unless I'm misunderstanding (quite likely), won't that change every second occurrence, not just the second one ?.
Yes but if you take out the c=0 at the end, problem solved
 
Old 04-07-2010, 09:17 PM   #9
ghostdog74
Senior Member
 
Registered: Aug 2006
Posts: 2,697
Blog Entries: 5

Rep: Reputation: 241Reputation: 241Reputation: 241
Quote:
Originally Posted by syg00 View Post
ghostdog74, unless I'm misunderstanding (quite likely), won't that change every second occurrence, not just the second one ?.
yes, i interpreted OP's req'd that way. If its only the 2nd instance, then as grail mentioned, remove the c=0
 
Old 05-02-2011, 02:38 PM   #10
typer100
LQ Newbie
 
Registered: May 2011
Posts: 1

Rep: Reputation: 0
Old thread, but I have another question. I'll use the current example...

testline="foo bar foo bar foo bar foo bar"

Let say I want to replace occurrence 2 and 4 with FOO. I can obviously this with 2 commands...

echo "$testline" | sed 's/foo/FOO/2'|sed 's/foo/FOO/3'
foo bar FOO bar foo bar FOO bar

It works but, any chase for a one liner... s/foo/FOO/2,4

Last edited by typer100; 05-02-2011 at 02:42 PM.
 
Old 05-02-2011, 03:30 PM   #11
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Code:
echo "$testline" | sed -r 's/(.*foo.*)foo(.*foo.*)foo/\1FOO\2FOO/'
foo bar FOO bar foo bar FOO bar
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Problem using sed to replace string in file umk Debian 12 02-01-2012 09:39 AM
Sed/awk/grep search for number string of variable length in text file Alexr Linux - Newbie 10 01-19-2010 02:34 PM
Using sed/awk to replace a string at a given position in anoopvraj Linux - Newbie 6 05-30-2009 08:59 AM
how to find and replace only the 2nd occurrence of similar string in a file hchoonbeng Linux - Newbie 1 10-08-2008 04:44 AM
SED replace string by occurrence uttam_h Programming 5 03-05-2008 11:02 PM


All times are GMT -5. The time now is 11:32 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration