ChatGPT解决这个技术问题 Extra ChatGPT

Replace whole line containing a string using Sed

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?


T
Todd A. Jacobs

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

Note that you need a space before the c\. I've just edited to add this.
@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.
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.
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…
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)
T
Thor

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

Thank you, got mine working: sed 's/.*.*/SE_LABEL = ABC/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
K
Karl Gjertsen

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.


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
C
Community

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

b
b_laoshi

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

G
Grain

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


C
Community

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


T
Tom
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


s
sjordi
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


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.
B
Benjamin W.

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

T
The Mauler

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


J
Jens

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

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.
S
Saravanan Sachi

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

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

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

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