Reputation: 1951
How can I make sed
filter matching lines according to some expression, but ignore non-matching lines, instead of letting them print?
As a real example, I want to run scalac
(the Scala compiler) on a set of files, and read from its -verbose
output the .class
files created. scalac -verbose
outputs a bunch of messages, but we're only interested in those of the form [wrote some-class-name.class]
.
What I'm currently doing is this (|&
is bash 4.0's way to pipe stderr to the next program):
$ scalac -verbose some-file.scala ... |& sed 's/^\[wrote \(.*\.class\)\]$/\1/'
This will extract the file names from the messages we're interested in, but will also let all other messages pass through unchanged! Of course we could do instead this:
$ scalac -verbose some-file.scala ... |& grep '^\[wrote .*\.class\]$' |
sed 's/^\[wrote \(.*\.class\)\]$/\1/'
which works but looks very much like going around the real problem, which is how to instruct sed
to ignore non-matching lines from the input. So how do we do that?
Upvotes: 131
Views: 68445
Reputation: 67859
If you don't want to print lines that don't match, you can use the combination of
-n
option which tells sed not to printp
flag which tells sed to print what is matchedThis gives:
sed -n 's/.../.../p'
Additionally, you can use a preceding matching pattern /match only these lines/
to only apply the replacement command to lines matching this pattern.
This gives:
sed -n '/.../ s/.../.../p'
e.g.:
# replace all occurrences of `bar` with `baz`
sed -n 's/bar/baz/p'
# replace `bar` with `baz` only on lines matching `foo`
sed -n '/foo/ s/bar/baz/p'
See also this other answer addressing Rapsey's comment below on multiple replacements
Upvotes: 304
Reputation: 6394
Rapsey raised a relevant point about multiple substitutions expressions.
{}
(separated with a semi-colon ;
or a new line)Syntax:
sed -n -e '/^given_regexp/ {s/regexp1/replacement1/flags1;[...];s/regexp1/replacement1/flagsnp}'
Example (see Here document for more details):
Code:
sed -n -e '/^ha/ {s/h/k/g;s/a/e/gp}' <<SAMPLE
haha
hihi
SAMPLE
Result:
keke
Upvotes: 4
Reputation: 42347
Another way with plain sed:
sed -e 's/.../.../;t;d'
s///
is a substituion, t
without any label conditionally skips all following commands, d
deletes line.
No need for perl or grep.
(edited after Nicholas Riley's suggestion)
Upvotes: 108