470

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?

0

16 Answers 16

672

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
Sign up to request clarification or add additional context in comments.

11 Comments

I found the \ after c is not irreplacable. without it the whole command can run perfectly. It was added here to increase readability?
Getting "sed: 1: "/tmp/foo": invalid command code f" on OS X +zsh.
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…
sed -i '/TEXT_TO_BE_REPLACED/c\'"$var" /tmp/foo
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)
|
219

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

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

7 Comments

Thank you, got mine working: sed 's/.*<expression>.*/<expression>SE_LABEL = ABC<expression>/g' MYR2.xml > test.txt
This is working on Mac OS X Yosemite with the exception that I am using the -i and -e flags as follows: sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
@KentJohnson I think you have mismatched quotes in your command.
@MBarnett you are right, I should have two double quotes there.
Just for the full info. To make it inplace one can add -i option
|
33

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.

2 Comments

you can still use single quotes like this: sed -i '/TEXT_TO_BE_REPLACED/c '"$VARIABLE"'' /tmp/foo
This variation worked on Ubuntu/Debian: sed -i "/TEXT_TO_BE_REPLACED/c\\$REPLACEMENT_TEXT_STRING" /tmp/foo
25

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

1 Comment

Another way around having to escape the slashes in sed is to use another delimiter. sed -i 's@/original/path/name@/new/path/name@' ./filename.txt is perfectly valid.
10

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/'

Comments

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

Comments

4

The question asks for solutions using sed, but if that's not a hard requirement then there is another option which might be a wiser choice.

The accepted answer suggests sed -i and describes it as replacing the file in-place, but -i doesn't really do that and instead does the equivalent of sed pattern file > tmp; mv tmp file, preserving ownership and modes. This is not ideal in many circumstances. In general I do not recommend running sed -i non-interactively as part of an automatic process--it's like setting a bomb with a fuse of an unknown length. Sooner or later it will blow up on someone.

To actually edit a file "in place" and replace a line matching a pattern with some other content you would be well served to use an actual text editor. This is how it's done with ed, the standard text editor.

printf '%s\n' '/TEXT_TO_BE_REPLACED/' d i 'This line is removed by the admin' . w q | \
ed -s /tmp/foo > /dev/null

Note that this only replaces the first matching line, which is what the question implied was wanted. This is a material difference from most of the other answers.

That disadvantage aside, there are some advantages to using ed over sed:

  • You can replace the match with one or multiple lines without any extra effort.
  • The replacement text can be arbitrarily complex without needing any escaping to protect it.
  • Most importantly, the original file is opened, modified, and saved. A copy is not made.

How it works

How it works:

  • printf will use its first argument as a format string and print each of its other arguments using that format, effectively meaning that each argument to printf becomes a line of output, which is all sent to ed on stdin.
  • The first line is a regex pattern match which causes ed to move its notion of "the current line" forward to the first line that matches (if there is no match the current line is set to the last line of the file).
  • The next is the d command which instructs ed to delete the entire current line.
  • After that is the i command which puts ed into insert mode;
  • after that all subsequent lines entered are written to the current line (or additional lines if there are any embedded newlines). This means you can expand a variable (e.g. "$foo") containing multiple lines here and it will insert all of them.
  • Insert mode ends when ed sees a line consisting of .
  • The w command writes the content of the file to disk, and
  • the q command quits.
  • The ed command is given the -s switch, putting it into silent mode so it doesn't echo any information as it runs,
  • the file to be edited is given as an argument to ed,
  • and, finally, stdout is thrown away to prevent the line matching the regex from being printed.

Some Unix-like systems may (inappropriately) ship without an ed installed, but may still ship with an ex; if so you can simply use it instead. If have vim but no ex or ed you can use vim -e instead. If you have only standard vi but no ex or ed, complain to your sysadmin.

3 Comments

I really like this answer but I cannot get it to work for the case where the search parameter is a variable. It does work when the new text to add is a variable. I've tried lots of combinations of quotes/hyphens, etc. Here's an example of what I'm trying: printf '%s\n' '/$txt_tb_replaced/' d i $replacewiththis . w q | \ed -s file.txt > /dev/null #Any idea how I can use this with a variable as the search parameter? Thanks! I'm using MacOS 15.0.
@Orestes: in shell the $ starts an expansion, but not inside single-quoted strings. If you double quote the string containing your attempt at variable expansion then it will work as expected. But, be careful expanding variables inside of regular expressions! You may inadvertently inject regex metacharacters which are not matched literally, or which break the expression (e.g. if the txt_tb_replaced variable contains a /). Be careful to also always doble quote expansions for safety. Working version: printf '%s\n' "/$txt_tb_replaced/" d i "$replacewiththis" . w q | \ed -s file.txt
CRITICAL flaw: If this script does not find the string, it sets its pointer to the end of the file. It then deletes and replaces that last line, even though it doesn't match.
3

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

Comments

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

Comments

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

Comments

2

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:
Mozart
Bach

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

Comments

1

This worked for me:

sed -i <extension> 's/.*<Line to be replaced>.*/<New line to be added>/'

An example is:

sed -i .bak -e '7s/.*version.*/      version = "4.33.0"/'
  • -i: The extension for the backup file after the replacement. In this case, it is .bak.
  • -e: The sed script. In this case, it is '7s/.*version.*/ version = "4.33.0"/'. If you want to use a sed file use the -f flag
  • s: The line number in the file to be replaced. In this case, it is 7s which means line 7.

Note:

If you want to do a recursive find and replace with sed then you can grep to the beginning of the command:

grep -rl --exclude-dir=<directory-to-exclude> --include=\*<Files to include> "<Line to be replaced>" ./ | sed -i <extension> 's/.*<Line to be replaced>.*/<New line to be added>/'

Comments

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

3 Comments

That changes FOO=TEXT_TO_BE_REPLACED to FOO=This line ... so does not meet the specification.
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
@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.
0
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 Comment

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

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

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

2 Comments

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.
What do you mean by "duplicate properties"? To negate matching an address you use ! address.
-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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.