16

I'm modifying some files and want to skip all lines that have the strings def or page. on them. How do I do this in sed?

user438383
  • 5,716
  • 8
  • 28
  • 43
dylam
  • 553
  • 3
  • 6
  • 16

2 Answers2

32

If I understood well, you want to apply some changes to various lines except some line matching a regex, right? In this case, let us suppose I have the following file:

$ cat file
this is a def
this has no d e f
this is a page by the way
but this is no p a g e as we know ito

We want to replace all this by that but ignore the lines containing by def or page. So first we delete the lines starting with def or page:

/def/d;/page/d;

Then we apply our operation as usual:

s/this/that/g

The result is:

$ sed '/def/d;/page/d;s/this/that/g' file
that has no d e f
but that is no p a g e as we know ito

But if by "skip" you mean "do not apply my operations", just negate the address:

$ sed -E '/(def|page)/!s/this/that/g' file
this is a def
that has no d e f
this is a page by the way
but that is no p a g e as we know ito

The above statement correct. Interestingly, the 'or' operator is associated with "extended regular expression." So you must specify -E for "extended regular expression" because sed, by default, uses only "basic regular expressions."

For example, the following statement doesn't work:

$ sed -e '/(def|page)/!s/[A-Za-z_]*login[A-Za-z_]*/page.&/g' < file > new_file

But this statement below works:

$ sed -E '/(def|page)/!s/[A-Za-z_]*login[A-Za-z_]*/page.&/g' < file > new_file
slm
  • 15,396
  • 12
  • 109
  • 124
brandizzi
  • 26,083
  • 8
  • 103
  • 158
  • 2
    Another way of avoiding modifying the `def` or `page` lines is `-e '/def/n;/page/n'` (which also avoids using not-strictly-portable extended regular expressions). The `n` command prints the current line and moves to the next line. – Jonathan Leffler Jul 14 '11 at 00:25
  • 1
    That is an awesome way, Jonathan! I was trying to convey some method for avoiding the extended regexes and considered to do something like `/def/!{/page/!s/this/that/g;}` file. It is a cumbersome solution however, and yours is much better. One more sed pattern to have in mind :) – brandizzi Jul 14 '11 at 01:24
  • @yoyoyo : yes, awesome answers indeed, don't forget to 'accept' the one you found most helpful. and when you get more reputation points, come back and vote up any/and all answers you found that improved your knowledge. Voting for other peoples answers does not reduce your reputation ;-) Good Luck! – shellter Jul 14 '11 at 16:56
  • @jonathan suggestion doesn't work if you have consecutive lines that match. For instance if the first `this is a def` line is repeated, it will only skip the first but do the change on the second. I think you need to do `-e '/def/n;/def/b;/page/n;/page/b'` – Dave Griffiths Jan 16 '15 at 14:31
  • @DaveGriffiths: you're right — drat! Manual says _"`[2addr]n` Write the pattern space to the standard output if the default output has not been suppressed, and replace the pattern space with the next line of input."_ It doesn't add the all important 'and start the next cycle' which I had assumed. The fix is to replace the `n` with `{p;d;}` which prints the line and deletes it, with the `d` command having _"`[2addr]d` Delete the pattern space and start the next cycle."_ – Jonathan Leffler Jan 16 '15 at 15:17
  • Can someone add an answer explaining how to use the information in these comments? – randy Jan 11 '16 at 20:17
2

AFAIK You can't (easily) negate matching lines with sed, but something like will almost work:

sed '/\([^d][^e][^f][^ ]\)\|\([^p][^a][^g][^e]\)/ s/foo/bar/' FILE

it replaces foo with bar on the lines which does not contain def or page but catch is that "matching" lines must be at least 4 char long.

A better solution is to use awk, e.g.:

awk '{ if ($0 !~ /def|page/) { print gensub("foo","bar","g") } else { print } }' FILE

HTH

Zsolt Botykai
  • 50,406
  • 14
  • 85
  • 110
  • 5
    on the contrary, it is easy to negate a regex address in sed! Just put a `!` at the front of the regex, such as in `/def/!s/foo/bar/`. – brandizzi Jul 14 '11 at 00:18