maimun4itu
maimun4itu

Reputation: 45

Bash script: Replace a line in a file

I have the following script:

#!/bin/bash

LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1"
NEW_LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1 elevator=deadline"

#sed -i "/${LINE}/c ${NEW_LINE}" grub.cfg

LINE1="insmod lvm"
NEW_LINE1="insmod lvm x"

sed -i "/${LINE1}/c ${NEW_LINE1}" grub.cfg

Can someone tell me why the commented sed does not work (Error: sed: -e expression #1, char 10: extra characters after command), but the uncommented one does, when only the strings are different?

Upvotes: 1

Views: 103

Answers (2)

ConMan
ConMan

Reputation: 1652

The uncommented line does not work because the delimiter your sed expression uses appears in the thing you are matching and replacing.

LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1"
NEW_LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1 elevator=deadline"

sed -i "/${LINE}/c ${NEW_LINE}" grub.cfg

Your sed expression will resolve to:

/linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1/c linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1 elevator=deadline

As you can see, this results in more operations in the expression than sed expects, hence your error message extra characters after command.

If you change your delimiter to a different character it should work:

sed -i "\:${LINE}:c ${NEW_LINE}" grub.cfg

You could also use the s command — try:

sed -i "s:${LINE}:c ${NEW_LINE}:g" grub.cfg

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 755064

Since there are slashes in the line that you're looking for, you need to use a different character to mark the boundaries of the s/// command. For example, you don't have any % symbols in the strings, so:

LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1"
NEW_LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1 elevator=deadline"

sed -i "s%.*${LINE}.*%${NEW_LINE}%" grub.cfg

Or, keeping the change command, you need to use a different character in the match — you use a backslash in front of it to indicate that's what you're doing.

LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1"
NEW_LINE="linux /disk0/vmlinuz rw root=/dev/mapper/VolGroup1-disk0_root console=tty0 clock=pit aacraid.msi=1 elevator=deadline"

sed -i "\%${LINE}%c ${NEW_LINE}" grub.cfg

The POSIX specification for sed says:

The sed utility shall support the BREs described in XBD Basic Regular Expressions, with the following additions:

  • In a context address, the construction "\cBREc", where c is any character other than <backslash> or <newline>, shall be identical to "/BRE/". If the character designated by c appears following a <backslash>, then it shall be considered to be that literal character, which shall not terminate the BRE. For example, in the context address "\xabc\xdefx", the second x stands for itself, so that the BRE is "abcxdef".

and:

[2addr]s/BRE/replacement/flags
Substitute the replacement string for instances of the BRE in the pattern space. Any character other than <backslash> or <newline> can be used instead of a <slash> to delimit the BRE and the replacement. Within the BRE and the replacement, the BRE delimiter itself can be used as a literal character if it is preceded by a <backslash>.

Upvotes: 1

Related Questions