13

I'm new to sed, so maybe someone can help me out. 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?

brandizzi
  • 24,902
  • 7
  • 100
  • 148
dylam
  • 503
  • 2
  • 6
  • 16

2 Answers2

30

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
  • 14,096
  • 12
  • 98
  • 116
brandizzi
  • 24,902
  • 7
  • 100
  • 148
  • 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
  • 48,485
  • 14
  • 85
  • 106
  • 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