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?

- 5,716
- 8
- 28
- 43

- 553
- 3
- 6
- 16
-
3This is a task much better suited to `grep`... – Chris Eberle Jul 13 '11 at 20:01
-
3@Chris Agreed. egrep -v 'def|page'
is a start. – bitbucket Jul 13 '11 at 20:22 -
1Do you want to skip the lines by not updating it or by not even presenting it at the output at all? – brandizzi Jul 14 '11 at 00:11
-
I'm not sure what's being asked here. Could you provide input and output? Is this part of a sed stream or are you literally rewriting `grep`? – Evan Carroll Jul 07 '18 at 17:07
2 Answers
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
-
2Another 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
-
1That 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
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

- 50,406
- 14
- 85
- 110
-
5on 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