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?
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:
sed -n 's/.../.../p'
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)
tx
and d
with a newline rather than a semicolon as I was getting undefined label 'x;d;:x'
.
sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'
(suggested in a comment on a similar question).
sed -e 's/.../.../' -e 't' -e 'd'
.
-e
option so dont mention about it generally.
Use Perl:
... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Rapsey raised a relevant point about multiple substitutions expressions.
First, quoting an Unix SE answer, you can "prefix most sed commands with an address to limit the lines to which they apply".
Second, you can group commands within curly braces {} (separated with a semi-colon ; or a new line)
Third, add the print flag p on the last substitution
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}' <
Result: keke
sed -n '/.../!p'
There is no need for a substitution.
sed '/.../ d'
for deleting lines that do match
Success story sharing
echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'
will output bothbar
andoof
on separate lines. Although the goto-label variety cannot handle multiple patterns either since it will delete the line if the first pattern does not match.