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?
-
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
- 48,485
- 14
- 85
- 106
-
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