Paggas
Paggas

Reputation: 1951

Have sed ignore non-matching lines

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

Answers (4)

mouviciel
mouviciel

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

This 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

Amessihel
Amessihel

Reputation: 6394

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}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Result:

    keke
    

Upvotes: 4

Obviously
Obviously

Reputation: 11

sed -n '/.../!p'

There is no need for a substitution.

Upvotes: 0

liori
liori

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

Related Questions