1

I want to delete all lines in a file that contain one pattern, but not another.

For example, if I have this file:

hello people foo
hello world bar
hello something
blah blah blah

I want to delete all lines that contain hello, but not world, so that my file looks like this:

hello world bar
blah blah blah

I tried the following:

sed -n '/hello/p' file | sed -i '/world/!d'

But I get the error message -i may not be used with stdin

David Ravetti
  • 2,030
  • 1
  • 17
  • 22
buydadip
  • 8,890
  • 22
  • 79
  • 154

6 Answers6

6

This should work

sed  -i '/hello/{/world/!d}' file
iruvar
  • 22,736
  • 7
  • 53
  • 82
  • for some reason it doesn't, I have a file called git, and I run the sed command as so: sed -i '/hello/{/world/!d}' git ... and I get this error:sed: 1: "git": extra characters at the end of g command – buydadip Jan 01 '15 at 21:19
  • 1
    Similar results as @Bolboa here, but error is `sed: 1: "testfile.txt": undefined label 'estfile.txt'`. Tested on OS X 10.9.5. – David Ravetti Jan 01 '15 at 21:24
5

A single sed invocation:

sed -n '/hello/ {/world/d; p}' file

For lines matching /hello/, if it also matches /world/ delete, else print

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • hmmm... this is the error I get: sed: 1: "/hello/ {/world/d; p}": extra characters at the end of p command... Any reason why? – buydadip Jan 01 '15 at 21:48
  • Excellent solution glenn. @Bolboa, check your syntax, worked perfectly on Linux exactly as `sed -n '/hello/ {/world/d; p}'` – David C. Rankin Jan 01 '15 at 21:53
  • this does not print the line 'blah blah blah' – repzero Jan 01 '15 at 21:54
  • @DavidC.Rankin Yeah, I really like the solution too, but I guess I should've mentioned that I am using OSX, does that make a difference? – buydadip Jan 01 '15 at 21:55
  • Oh should be `sed -n '/hello/ {/world/d};p'` now it works perfect... (Glenn, take a look an perhaps tweak your answer) – David C. Rankin Jan 01 '15 at 21:55
  • 1
    @Bolboa, check out the OSX sed man page: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/sed.1.html – glenn jackman Jan 02 '15 at 00:53
  • this deleted line containing world. Except I have replaced word with '172.16.' – Dimitri Kopriwa May 05 '17 at 08:39
  • This does the exact opposite of what the question asked for. This prints *no* line (because of `-n`) except those that contain “hello” (because of `p`) and not “world” (because of `d`). A working invocation is `sed '/hello/ {/world/p; d}'`. It works as follows. For any line that contains “hello”, firstly we print it immediately (`p`) *if* it also contain “world”, then (`;`) we discard it (`d`) anyway. For any other line, we print it untouched. – Maëlan Feb 08 '20 at 15:45
4

an awk alternative:

awk '!/hello/ ||/world/' file
Kent
  • 189,393
  • 32
  • 233
  • 301
1
sed -i.bak '/hello/{/world/!d}

this answer is different since an extension is provided to -i.

I the thread below should be useful

sed command with -i option failing on Mac, but works on Linux

Community
  • 1
  • 1
repzero
  • 8,254
  • 2
  • 18
  • 40
  • This is just the same as 1_CR posted 20 minutes before you. – Jotne Jan 02 '15 at 07:34
  • @Jotne Yes.. i think the -i without an extension could be the reason why the OP is getting an error using this simple solution (an assumption since I don't have OS X). Slight difference. – repzero Jan 02 '15 at 10:24
1

Just use awk to keep the logic simply as-stated:

$ awk '/hello/ && !/world/{next} 1' file
hello world bar
blah blah blah
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

After playing a round a bit, I was able to procure one script implementing sed, which worked for me:

file=$1
patt=$(sed -n '/world/p' $file)
sed -n "/hello/!p;/$patt/p" $file
buydadip
  • 8,890
  • 22
  • 79
  • 154