How to use sed to replace only the first occurrence in a file?

David Dibben picture David Dibben · Sep 29, 2008 · Viewed 362.3k times · Source

I would like to update a large number of C++ source files with an extra include directive before any existing #includes. For this sort of task, I normally use a small bash script with sed to re-write the file.

How do I get sed to replace just the first occurrence of a string in a file rather than replacing every occurrence?

If I use

sed s/#include/#include "newfile.h"\n#include/

it replaces all #includes.

Alternative suggestions to achieve the same thing are also welcome.

Answer

tim picture tim · Feb 26, 2012

A sed script that will only replace the first occurrence of "Apple" by "Banana"

Example

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

This is the simple script: Editor's note: works with GNU sed only.

sed '0,/Apple/{s/Apple/Banana/}' input_filename

The first two parameters 0 and /Apple/ are the range specifier. The s/Apple/Banana/ is what is executed within that range. So in this case "within the range of the beginning (0) up to the first instance of Apple, replace Apple with Banana. Only the first Apple will be replaced.

Background: In traditional sed the range specifier is also "begin here" and "end here" (inclusive). However the lowest "begin" is the first line (line 1), and if the "end here" is a regex, then it is only attempted to match against on the next line after "begin", so the earliest possible end is line 2. So since range is inclusive, smallest possible range is "2 lines" and smallest starting range is both lines 1 and 2 (i.e. if there's an occurrence on line 1, occurrences on line 2 will also be changed, not desired in this case). GNU sed adds its own extension of allowing specifying start as the "pseudo" line 0 so that the end of the range can be line 1, allowing it a range of "only the first line" if the regex matches the first line.

Or a simplified version (an empty RE like // means to re-use the one specified before it, so this is equivalent):

sed '0,/Apple/{s//Banana/}' input_filename

And the curly braces are optional for the s command, so this is also equivalent:

sed '0,/Apple/s//Banana/' input_filename

All of these work on GNU sed only.

You can also install GNU sed on OS X using homebrew brew install gnu-sed.