65

I'm trying to find a line in a file and replace the next line with a specific value. I tried sed, but it seems to not like the \n. How else can this be done?

The file looks like this:

<key>ConnectionString</key>
<string>anything_could_be_here</string>

And I'd like to change it to this

<key>ConnectionString</key>
<string>changed_value</string>

Here's what I tried:

sed -i '' "s/<key>ConnectionString<\/key>\n<string><\/string>/<key>ConnectionString<\/key>\n<string>replaced_text<\/string>/g" /path/to/file
bswinnerton
  • 4,533
  • 8
  • 41
  • 56

3 Answers3

79

One way: Sample file

$ cat file
Cygwin
Unix
Linux
Solaris
AIX

Using sed, replacing the next line after the pattern 'Unix' with 'hi':

$ sed '/Unix/{n;s/.*/hi/}' file
Cygwin
Unix
hi
Solaris
AIX

For your specific question:

$ sed '/<key>ConnectionString<\/key>/{n;s/<string>.*<\/string>/<string>NEW STRING<\/string>/}' your_file
<key>ConnectionString</key>
<string>NEW STRING</string>
Guru
  • 16,456
  • 2
  • 33
  • 46
  • 1
    I get `sed: 1: "/ConnectionString< ...": bad flag in substitute command: '}'` when running `sed '/ConnectionString<\/key>/{n;s/.*/test<\/string>/}' $HOME/Documents/temp.rdp` – bswinnerton Sep 04 '13 at 17:32
  • 18
    @bswinnerton Ran into the same problem on OS X and this hint helped: http://stackoverflow.com/a/4904510/479478. So essentially you need to break the curly braces into pieces: ``sed -i '' -e '/ConnectionString<\/key>/ {' -e 'n; s/.*<\/string>/changed<\/string>/' -e '}' your_file`` – Eric Chen Sep 20 '15 at 14:04
  • sed -i '' -e '/Unix/ {' -e 'n; s/.*/hihi/' -e '}' – kalan nawarathne Jul 08 '17 at 15:51
  • 2
    @EricChen this is amazing... I have no idea how (or better say why) it works... but indeed it does! – estani Aug 11 '21 at 11:48
  • On Linux, in absence of command line utility like mac OS "plutil", this command is useful to manipulate XML based property files (e.g. Info.plist) used within iOS applications. – Kishan Parekh Nov 03 '21 at 06:36
57

This might work for you (GNU sed):

sed '/<key>ConnectionString<\/key>/!b;n;c<string>changed_value</string>' file

!b negates the previous address (regexp) and breaks out of any processing, ending the sed commands, n prints the current line and then reads the next into the pattern space, c changes the current line to the string following the command.

miken32
  • 42,008
  • 16
  • 111
  • 154
potong
  • 55,640
  • 6
  • 51
  • 83
  • 4
    If there's any osx user interested, you can use gsed insted of sed to make it work (install it with macport or `brew install gnu-sed`) – txulu Feb 13 '15 at 13:09
  • 2
    Can you breakdown what `!b` `n` and `c` do here? – ffledgling Apr 12 '17 at 14:58
  • 9
    @ffledgling, `!b` negates the previous address (regexp) and breaks out of any processing, ending the sed commands, `n` prints the current line and then reads the next into the pattern space, `c` changes the current line to the string following the command. – potong Apr 12 '17 at 21:58
  • @txulu good heavens, that's helpful. I was pulling my hair out on my mac. – Chris C Nov 19 '18 at 20:31
  • @potong whoa thanks! this is precisely what i needed! – chrishuen Jan 25 '19 at 15:51
  • Is there a way to limit the sed action to the first match of the pattern? – badkya May 04 '19 at 22:41
  • @badkya sure, prefix the above command by `-e` and a space and then add the following extra command set `-e ':a;n;ba'` before the `file` argument. This will read/print all following lines to the end of the file. – potong Oct 18 '19 at 13:11
  • @miken32 Can you direct me to a resource to understand the syntax here? What is meant by "`!b` negates the previous address (regexp) and breaks out of any processing" How do all fit together here? What is the general syntax of the sed instruction? – Iresh Dissanayaka Aug 22 '22 at 12:09
  • @IreshDissanayaka sorry, not my answer. Personally, I'd do this with `awk`. I'm sure the `sed` man page will explain what everything does. – miken32 Aug 22 '22 at 14:15
  • @IreshDissanayaka the `!` refers to the previous regexp which it negates i.e. if it does not match the regexp. The `b` is a branch command, when it has no argument it breaks out of all sed commands and goes to the end of the current sed cycle. See [here](https://www.gnu.org/software/sed/manual/sed.html#Addresses-overview) and [here](https://www.gnu.org/software/sed/manual/sed.html#Branching-and-flow-control) for further details. – potong Aug 22 '22 at 18:06
6

It works. Additionaly is interested to mention that if you write,

sed '/<key>ConnectionString<\/key>/!b;n;n;c<string>changed_value</string>' file

Note the two n's, it replaces after two lines and so forth.

  • 6
    @Mausy5043 `-i` does not suppress output. That is the inline replace flag. This flag allows you to directly modify the file. – Daniel Owsley Mar 23 '18 at 16:33
  • @DanielOwsley correct so *effectively* output to the terminal is suppressed ;-) – Mausy5043 Mar 24 '18 at 18:08
  • 6
    That's a a **very** poor way to describe the effect of using `-i`, because "modify the original file" is a far more important effect than "suppress the output". If something is going to alter your files that's important to know (a bad `sed` command could cause you to lose data). – Jonathan Wakely Apr 09 '18 at 18:58
  • 1
    @Mausy5043 tread lightly with `sed`, if you use `-i` to "suppress" output, you WILL ruin your year... possibly make the news. Its like saying: Use `rm -rf /` to instead of `sleep 20` - both will pause for a while before returning the prompt... but with VERY different outcomes. – Okezie Nov 26 '19 at 16:47