Is there a way to use sed to remove only the exact string match?

Viewed 1911

I have recently started learning bash and I ran into a problem doing an assignment, So I have a txt file and in it contains something like

foo:abc:200:1:1:1
foobar:asd:100:3:2:1
bar:test:100:2:2:2

where the first column is the title of the book followed by the author name followed by price,quantity available and qty sold all seperated with the delimiter ":"

the goal here is to remove a book base on the name and author the user types in.

I have searched around and found that sed might possibly be able to help me with this problem, I have tried to test sed by deleting base on the title alone with

sed /"foo"/d Book.txt

I expected the output to be

foobar:asd:100:3:2:1
bar:test:100:2:2:2

however the output was

bar:test:100:2:2:2   

which tells me that any line in the txt file containing "foo" will get deleted

Hence I would like to ask

  1. Is there any way to use sed so it deletes the exact match only instead of lines containing foo?
  2. is there any way to use delimiters with sed so I can use both title and author?
  3. Should I be using something other than sed?
3 Answers

Using sed it is better to use:

sed -E '/(^|:)foo(:|$)/d' file

foobar:asd:100:3:2:1
bar:test:100:2:2:2

Which makes sure foo is preceded by start or : and followed by end or :.

However this job is more suitable for awk as data is delimited by colon:

awk -F: '$1 != "foo"' file
  1. Is there any way to use sed so it deletes the exact match only instead of lines containing foo?

Yes you can for the given example, if you mark your search pattern to match exactly foo: you can have luck deleting it. For e.g. if you do below

sed '/^foo:/d' file

The pattern ^ marks that the string starting with foo followed by a colon mark : which matches your use-case. This is assuming foo can be part of the fist column only

  1. Is there any way to use delimiters with sed so I can use both title and author?
  2. Should I be using something other than sed?

If you are dealing with a input file has a fixed de-limiter like : which will never form a part of your valid column content, then using awk/perl are better suited as they read text easily once a de-limiter is set.

As an example, consider an e.g. if you want to change the quantity name from fourth column for one particular book named foobar, with awk you can just do

awk -F: 'BEGIN { OFS = FS } $1 == "foobar" { $4 = 6 }1' input-file

To decode above line, the content within '..' are left untouched by the shell and passed literally to the command, that's why we wrap the content in single quotes. Also the statements inside it are not meaningful in the context of the shell.

So the -F: sets the input field-separator to : which is when the command reads the file line by line, the first line is broken down into tokens separated by :. The first column is labelled $1, which is extended up to $NF, meaning the last column of the line. The part BEGIN { OFS = FS } assigns the output field separator as the same as input i.e. retain the : de-limitation when awk writes the output also.

The part $1 == "foobar" { $4 = 6 } is almost self-explanatory in a sense, that if the first column contains the string within quotes do the action inside {..}, which is set the fourth column value as 6. The {..}1 is a short-hand notation for {...; print} which is to re-construct the line based on the output field/record separators defined.

This might work for you (GNU sed):

sed '/\<foo\>/d' file

Or

sed '/\bfoo\b/d' file

The first solution uses \< start word and \> end word. The second solution uses the \b word boundary.

P.S. The dual of \b is \B so to delete lines that contain foobar or foobaz but not foo only, use:

sed '/\bfoo\B/d' file
Related