Edit file based on existence of a string

I have an input file, say, file1, which appears as follows:

WRITE=2
START=1
SMEAR=0
MIN=6
MAX=100

My problem is to search for the string SMEAR and if the string exists replace the entire line with SMEAR=-5. However, if the SMEAR does not exist, then I need to insert a new line SMEAR=-5. Ultimately the line SMEAR=-5 should appear only once.
I can replace the string using sed:

 sed -i '/SMEAR/cSMEAR=-5' file1

Or I can insert the string as a new line

 sed -i '10i SMEAR=-5' file1

But how can I combine them based on the existence of the string in the file?

EDIT:
Also it would be helpful if I can pass the value of SMEAR as a variable.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

It appears that the sequence of the lines doesn’t matter for your use case. Given that, I would use ex and simply:

  1. Remove all instances of SMEAR;
  2. Insert the line you want.

You can do this like so:

printf '%sn' 'g/SMEAR/d' '$a' 'SMEAR=-5' . x | ex file.txt

The first command is to globally delete all lines which match the regex /SMEAR/.

The next command is to append after the last line ($) the line SMEAR=-5. The . ends the text to be appended.

The x command saves changes and exits.

Each command is terminated by a newline by using printf '%sn' to send them to ex.


Also see this very similar solution which I wrote a while back on the vi/Vim stack exchange.


To test the changes by printing the changed file to the command line without saving the changes, replace the x with the two commands %p 'q!' like so:

printf '%sn' 'g/SMEAR/d' '$a' 'SMEAR=-5' . %p 'q!' | ex file.txt

% means “entire buffer,” which is what gets printed.

q! means “quit, discarding changes.”

To save the changes into a new file, replace the %p with w newfile.txt like so:

printf '%sn' 'g/SMEAR/d' '$a' 'SMEAR=-5' . 'w newfile.txt' 'q!' | ex file.txt

This writes the modified buffer into newfile.txt.

Alternatively you could do this at the start, to make a backup, and then save the changed file contents to the original location, file.txt, like so:

printf '%sn' 'w file.txt.bak' 'g/SMEAR/d' '$a' 'SMEAR=-5' . x | ex file.txt

Edit: Actually you don’t need to use q!; simply omitting the x is enough to avoid saving changes. When ex gets an EOF on trying to read further input, it will exit, and will not save changes.

Method 2

You could try like this: if a line matches just copy it to the hold space then substitute the value.
On the la$t line exchange hold space and pattern space then check if the latter is empty. If it’s not empty, it means the substitution was already made so nothing to do. If it’s empty, that means no match was found so replace the pattern space with SMEAR=-5 then append to the current line in the hold buffer. Finally, exchange again:

sed '/SMEAR/{h;s/.*/SMEAR=-5/};${x;/^$/{s//SMEAR=-5/;H};x}' infile

The above is gnu sed syntax. Portable:

sed '/SMEAR/{
h
s/.*/SMEAR=-5/
}
${
x
/^$/{
s//SMEAR=-5/
H
}
x
}' infile

Alternate way with ed:

ed -s infile<<IN || printf %s\n SMEAR=-5 >> infile
/SMEAR.*/s//SMEAR=-5/ 
w
q
IN

If no line matches pattern, ed will error out so the second command (printf...) will be executed to append the line to the file. Otherwise ed will edit the file in-place.


If you have the value of SMEAR saved in variable e.g.

var=-7

you would run:

sed '/SMEAR/{h;s/.*/SMEAR='"$var"'/};${x;/^$/{s//SMEAR='"$var"'/;H};x}' infile

or

ed -s infile<<IN || printf %s\n "SMEAR=${var}" >> infile
/SMEAR.*/s//SMEAR=${var}/ 
w
q
IN

Method 3

Or, implementing Wildcard’s idea
(remove any/all existing line(s) that contain SMEAR,
and then insert the line you want) with stone-age tools:

(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') > file1.tmp  &&  cp file1.tmp file1  &&  rm file1.tmp

or, if you aren’t concerned
about preserving the attributes (e.g., permissions) of
and hard links to file1, you could do

(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') > file1.tmp  &&  mv file1.tmp file1

or, if you have sponge, you could do

(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') | sponge file1


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x