Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Consider I have the following file:

echo "1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
" > ztest

Here I'd like to change only the first 1 into 5 and leave everything else intact.

I know sed has a quit command - so I'm trying the following:

$ sed 's/1/{5;q}/' ztest 
{5;q}
2
3
4
{5;q}
2
3
4
{5;q}
2
3
4
{5;q}
2
3
4

This changes all lines, so it's no good. So, then I try this:

$ sed '{s/1/5/;q}' ztest 
5

Now, this apparently does as requested - changes the first line and exits; however, I'd like the rest of the lines intact! (since this, basically has the effect of replacing the entire file with a single '5')

So I'm at a loss at what kind of syntax is needed? Note that I need this to change an "early" line in a gigabyte file inline (using sed -i); thus I'd like sed to only search and replace in the first brief section - and after that, output lines unchanged (since sed -i anyways dumps into a temp file first, and then overwrites the original); hoping to save some processing time.

Thanks in advance for any answers,
Cheers!

EDIT: forgot subquestion: is it possible to extend this to first n matches? (say, in the file above, first two '1's are changed to 5 - rest of it is output verbatim?)

share|improve this question

migrated from stackoverflow.com Feb 26 '12 at 19:34

This question came from our site for professional and enthusiast programmers.

    
Are you stuck with sed only? –  Mat Feb 26 '12 at 18:43
    
HI @Mat - not really; just the first one I resort to when I have t replace inline in text files - any other suggestions are welcome! –  sdaau Feb 26 '12 at 18:46
1  
same situation here –  selman Feb 26 '12 at 19:11
    
Many thanks for that link, @selman - I like the trick with line 0 (as in 0,/Apple/{s/Apple/Banana/}) from there - cheers! –  sdaau Feb 26 '12 at 19:58
add comment

5 Answers 5

up vote 3 down vote accepted

I have seen it somewhere else on this site:

sed '/1/{s/1/5/;:a;n;ba}' ztest

So once you found the 1st occurrence, you loop till the end of the file reading and printing lines.

Update

It can be enhance to only replace the Nth match. The idea is to store a X at each match in the holdspace, and when all the Xs are there, loop till the end of file. Bellow, the script that replace the 2nd occurrence:

sed '/1/{G;s/\nX\{1\}//;tend;x;s/^/X/;x;P;d};p;d;:end;s/1/5/;:a;n;ba'

Note that you need to put (N-1) between the \{ and \}.

Update 2

I realize that the P;d above is useless if the p;d is replaced by P;d. So a simpler solution is:

sed '/1/{G;s/\nX\{1\}//;tend;x;s/^/X/;x};P;d;:end;s/1/5/;:a;n;ba'
share|improve this answer
    
Many thanks for that @jfgagne - I was hoping sed can refer to the n-th match; and glad to see an example (I'll need to brush up on my sed syntax, though :) ) Cheers! –  sdaau Feb 26 '12 at 19:54
    
super! thanks @jfgagne for your clear explanation! –  xpt Jul 22 '13 at 18:55
add comment

Don't know if that's doable with sed, but here's an awk version.

awk 'BEGIN {matches=0}
     matches < 2 && /1/ { sub(/1/,"5"); matches++ }
     { print $0 }' ztest 

This will replace 1 with 5 for the first two matches of 1. (Change the 2 in there to increase/decrease the number of matches you want.)
After that, the file is printed as-is.

share|improve this answer
    
Many thanks for that, @Mat - by the way, your original sed answer led me this answer; but I think I like the direct number of matches specification better. Cheers! –  sdaau Feb 26 '12 at 19:53
add comment

This might work for you (GNU sed):

sed '0,/1/s//5/' file
share|improve this answer
    
Many thanks for that, @potong - cheers! –  sdaau Feb 26 '12 at 23:58
    
Thanks @potong, care to explain a bit more please? –  xpt Jul 22 '13 at 18:53
add comment

Use the :a;n;ba pattern once again:

$ sed '/1/{s//5/;:A;/1/{s//5/;:B;n;bB};n;bA}'
share|improve this answer
    
Many thanks for that pattern, @kev - cheers! –  sdaau Feb 27 '12 at 6:27
add comment

Thanks to @Mat's first answer (over at StackOverflow, now deleted), the syntax for sed to replace only the match on first line is:

sed  '1s/1/5/' ztest

However, you have to know explicitly the line number where to perform the match.

So if I know the file is as above, then the first two 1's appear on lines 1 and 5; so for the first two matches, I could instruct sed to apply the expression between lines 1 and (say) 6 - the syntax for line (address) range in sed being a comma (,):

$ sed  '1,6s/1/5/' ztest
5
2
3
4
5
2
3
4
1
2
3
4
1
2
3
4

Similarly, second and third occurrence of 1 are on lines 5 and 9 - so I can similarly use the range, say, from line 3 to line 10 to change second and third occurrence of 1:

$ sed  '3,10s/1/5/' ztest
1
2
3
4
5
2
3
4
5
2
3
4
1
2
3
4

.... which I guess answers my question in terms of sed; although I hoped that I could specify the number of matches directly (as in the awk solution by @Mat).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.