376
votes

I have a text file which has a particular line something like

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

I need to replace the whole line above with

This line is removed by the admin.

The search keyword is TEXT_TO_BE_REPLACED

I need to write a shell script for this. How can I achieve this using sed?

14

14 Answers

566
votes

You can use the change command to replace the entire line, and the -i flag to make the changes in-place. For example, using GNU sed:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
178
votes

You need to use wildards (.*) before and after to replace the whole line:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
22
votes

The Answer above:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

Works fine if the replacement string/line is not a variable.

The issue is that on Redhat 5 the \ after the c escapes the $. A double \\ did not work either (at least on Redhat 5).

Through hit and trial, I discovered that the \ after the c is redundant if your replacement string/line is only a single line. So I did not use \ after the c, used a variable as a single replacement line and it was joy.

The code would look something like:

sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo

Note the use of double quotes instead of single quotes.

20
votes

The accepted answer did not work for me for several reasons:

  • my version of sed does not like -i with a zero length extension
  • the syntax of the c\ command is weird and I couldn't get it to work
  • I didn't realize some of my issues are coming from unescaped slashes

So here is the solution I came up with which I think should work for most cases:

function escape_slashes {
    sed 's/\//\\\//g' 
}

function change_line {
    local OLD_LINE_PATTERN=$1; shift
    local NEW_LINE=$1; shift
    local FILE=$1

    local NEW=$(echo "${NEW_LINE}" | escape_slashes)
    sed -i .bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
    mv "${FILE}.bak" /tmp/
}

So the sample usage to fix the problem posed:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
7
votes

All of the answers provided so far assume that you know something about the text to be replaced which makes sense, since that's what the OP asked. I'm providing an answer that assumes you know nothing about the text to be replaced and that there may be a separate line in the file with the same or similar content that you do not want to be replaced. Furthermore, I'm assuming you know the line number of the line to be replaced.

The following examples demonstrate the removing or changing of text by specific line numbers:

# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
#   just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE

# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
4
votes

for manipulation of config files

i came up with this solution inspired by skensell answer

configLine [searchPattern] [replaceLine] [filePath]

it will:

  • create the file if not exists
  • replace the whole line (all lines) where searchPattern matched
  • add replaceLine on the end of the file if pattern was not found

Function:

function configLine {
  local OLD_LINE_PATTERN=$1; shift
  local NEW_LINE=$1; shift
  local FILE=$1
  local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
  touch "${FILE}"
  sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
  if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
  then
    echo "${NEW_LINE}" >> "${FILE}"
  fi
}

the crazy exit status magic comes from https://stackoverflow.com/a/12145797/1262663

2
votes

In my makefile I use this:

@sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md

PS: DO NOT forget that the -i changes actually the text in the file... so if the pattern you defined as "Revision" will change, you will also change the pattern to replace.

Example output:

Abc-Project written by John Doe

Revision: 1190

So if you set the pattern "Revision: 1190" it's obviously not the same as you defined them as "Revision:" only...

2
votes
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$ 
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'

it works fine

1
votes
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file    
done 

find_replace file contains 2 columns, c1 with pattern to match, c2 with replacement, the sed loop replaces each line conatining one of the pattern of variable 1

1
votes

To do this without relying on any GNUisms such as -i without a parameter or c without a linebreak:

sed '/TEXT_TO_BE_REPLACED/c\
This line is removed by the admin.
' infile > tmpfile && mv tmpfile infile

In this (POSIX compliant) form of the command

c\
text

text can consist of one or multiple lines, and linebreaks that should become part of the replacement have to be escaped:

c\
line1\
line2
s/x/y/

where s/x/y/ is a new sed command after the pattern space has been replaced by the two lines

line1
line2
1
votes

To replace whole line containing a specified string with the content of that line

Text file:

Row: 0 last_time_contacted=0, display_name=Mozart, _id=100, phonebook_bucket_alt=2
Row: 1 last_time_contacted=0, display_name=Bach, _id=101, phonebook_bucket_alt=2

Single string:

$ sed 's/.* display_name=\([[:alpha:]]\+\).*/\1/'
output:
100
101

Multiple strings delimited by white-space:

$ sed 's/.* display_name=\([[:alpha:]]\+\).* _id=\([[:digit:]]\+\).*/\1 \2/'
output:
Mozart 100
Bach 101

Adjust regex to meet your needs

[:alpha] and [:digit:] are Character Classes and Bracket Expressions

0
votes

It is as similar to above one..

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
0
votes

Below command is working for me. Which is working with variables

sed -i "/\<$E\>/c $D" "$B"
-1
votes

I very often use regex to extract data from files I just used that to replace the literal quote \" with // nothing :-)

cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt