tags:

views:

161

answers:

4

How can I make sed filter matching lines according to some expression, but ignore non-matching lines, instead of letting them slip?

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?

A: 

Use Perl:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Greg Hewgill
I hate that you're right!
Oren Mazor
+2  A: 

Why would using grep to filter lines be "going around the problem"? That's exactly what grep was designed to do, and pipes were designed to be chained. Your second option is fine.

Amber
+3  A: 

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 print
  • p flag which tells sed to print what is matched

This gives:

scalac -verbose some-file.scala ... |& sed -n 's/^\[wrote \(.*\.class\)\]$/\1/p'
mouviciel
+1  A: 

Another way with plain sed:

sed -e 's/.../.../;tx;d;:x'

s/// is a substituion, tx branches to label x if the substitution was successful, d deletes line, :x is a marker for label x.

No need for perl or grep.

liori
I like this answer the most :)
Paggas