395

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?

Jens
  • 65,924
  • 14
  • 115
  • 171
Rahul
  • 5,013
  • 6
  • 17
  • 12

14 Answers14

589

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
Todd A. Jacobs
  • 76,463
  • 14
  • 137
  • 188
  • 4
    Note that you need a space before the c\. I've just edited to add this. – Marcus Downing Mar 17 '14 at 12:58
  • 39
    @MarcusDowning GNU sed does *not* require the space; it works just fine as originally posted. If your particular sed requires the space, then by all means note which sed is incompatible and add the necessary invocation as a comment. However, please don't change working code in an accepted answer. – Todd A. Jacobs Mar 17 '14 at 14:08
  • 1
    I found the \ after c is not irreplacable. without it the whole command can run perfectly. It was added here to increase readability? – Zen Jun 22 '14 at 08:36
  • There is a trouble with " c " command, it can change only 1 line. So if you have multiple lines need to be changed, it will convert it into 1 line. – Zen Jun 26 '14 at 04:35
  • 7
    How can I use a variable instead of the text "This..."? If I replace it by $variable, it does not print its content but the variable name. – Steven Nov 26 '14 at 16:47
  • @Steven That is a separate question, even if it's related. Please [ask a new question](http://stackoverflow.com/questions/ask). – Todd A. Jacobs Nov 26 '14 at 16:50
  • I figured it out already. It was a question of escaping. Not sure if I will open a new one. There have been enough resources out there :) @CodeGnome – Steven Nov 27 '14 at 08:41
  • 1
    Getting "sed: 1: "/tmp/foo": invalid command code f" on OS X +zsh. – Olivier Lalonde Aug 20 '15 at 14:28
  • 23
    There is a problem with `c\ ` when followed directly by a variable: `…c\$VAR…` The backslash will escape the dollar. In this case I (bash/sed on Ubuntu 15.10) had to write `…c\\$VAR…` – Jan Nov 09 '15 at 09:45
  • 2
    `sed -i '/TEXT_TO_BE_REPLACED/c\'"$var" /tmp/foo` – jackal Aug 28 '17 at 08:16
  • 7
    on a mac use: `sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo` ; (when the first param is blank it edits in-file, otherwise creates a backup) – AndreDurao Apr 10 '18 at 11:19
  • In my experience with sed you need the s sed command together with the -i flag to substitute in place. `sed -i 's/TEXT_TO_BE_REPLACED/c\This line is removed by the admin./' /tmp/foo` – sebisnow Jul 31 '18 at 03:52
  • This is working in my ubuntu machine, but not in my macbook. – Haneef Mohammed Mar 01 '19 at 06:30
  • This didn't work for me on Mac. Used the following: sed -i '' "s/Text to replace.*/New text/" index.md – Prabath Siriwardena Jul 15 '19 at 08:25
  • Note that -i can't be used with standard in. – Olsonist Sep 21 '20 at 15:53
  • This is a great answer – jamesioppolo Jan 19 '21 at 03:02
189

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./'
Thor
  • 42,211
  • 10
  • 116
  • 125
25

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.

Karl Gjertsen
  • 4,435
  • 8
  • 36
  • 62
user4256255
  • 351
  • 3
  • 3
23

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)
    # FIX: No space after the option i.
    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
Community
  • 1
  • 1
skensell
  • 1,403
  • 12
  • 20
8

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/'
b_laoshi
  • 395
  • 5
  • 11
4

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

Grain
  • 553
  • 4
  • 5
2

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...

Community
  • 1
  • 1
Martin Pfeffer
  • 12,042
  • 8
  • 56
  • 66
2
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

Tom
  • 4,186
  • 6
  • 33
  • 49
nkl
  • 21
  • 1
1
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

sjordi
  • 19
  • 1
  • No, this is wrong on several counts. Run `sed` once with a script file containing all the replacements you want to perform. Running `sed -i` on the same file repeatedly is a horrible antipattern. – tripleee Dec 12 '18 at 06:46
1

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
Benjamin W.
  • 38,596
  • 16
  • 96
  • 104
1

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

The Mauler
  • 91
  • 4
0

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./'
Jens
  • 65,924
  • 14
  • 115
  • 171
  • 3
    That changes `FOO=TEXT_TO_BE_REPLACED` to `FOO=This line ...` so does not meet the specification. – Jens May 30 '13 at 09:18
  • Yes.. Our requirement is to replace entire line with 'This line is removed by the admin.' if we found the key pattren 'TEXT_TO_BE_REPLACED'. The above command is satisfying. Correct me if my understanding is wrong.. @Jens – Annapureddy Hari Jun 06 '13 at 14:20
  • @AnnapureddyHari this answer does not work if the text before or after the search string has anything in it besides A-Za-z0-9. It fails if there is an equals sign, as Jens pointed out. The "FOO=" portion will remain; you have not replaced the entire line. This code is short sighted about what might be in the file. If you mean wildcard, you should put wildcard, as Thor's answer shows. – msouth May 07 '16 at 21:37
0

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

sed -i "/\<$E\>/c $D" "$B"
Saravanan Sachi
  • 2,532
  • 5
  • 30
  • 41
  • But my new requirement is to SKIP commented (starts with #) line while replacing. As we are replacing complete line this will replace commented lines as well and you will end up with duplicate properties. If anyone has solution for this please let me know. – Ritesh Borkar Sep 19 '19 at 09:24
  • What do you mean by "duplicate properties"? To negate matching an address you use `! address`. – Rajib Oct 26 '20 at 15:50
-1

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
agold
  • 5,872
  • 9
  • 40
  • 53
staM
  • 9