kimon
kimon

Reputation: 2505

Opposite of (foo|bar|baz)

I'd like a regex to match everything but a few specific options within a broader expression.

The following example will match test_foo.pl or test_bar.pl or test_baz.pl:

/test_(foo|bar|baz)\.pl/

But I'd like just the opposite:

match test_.*\.pl except for where .* = (foo|bar|baz)

I'm kind of limited in my options for this because this is not directly into a perl program, but an argument to cloc, a program that counts lines of code (that happens to be written in perl). So I'm looking for an answer that can be done in one regex, not multiple chained together.

Upvotes: 2

Views: 373

Answers (4)

ikegami
ikegami

Reputation: 386331

(?:(?!STR).)*

is to

STR

as

[^CHAR]

is to

CHAR

So you want

if (/^test_(?:(?!foo|bar|baz).)*\.pl\z/s)

More readable:

my %bad = map { $_ => 1 } qw( foo bar baz );

if (/^test_(.*)\.pl\z/s && !$bad{$1})

Upvotes: 1

Sinan Ünür
Sinan Ünür

Reputation: 118148

#!/usr/bin/env perl

use strict; use warnings;

my $pat = qr/\Atest_.+(?<!foo|bar|baz)[.]pl\z/;

while (my $line = <DATA>) {
    chomp $line;
    printf "%s %s\n", $line, $line =~ $pat ? 'matches' : "doesn't match";
}


__DATA__
test_bar.pl
test_foo.pl
test_baz.pl
test baz.pl
0test_bar.pl
test_me.pl
test_me_too.txt

Output:

test_bar.pl doesn't match
test_foo.pl doesn't match
test_baz.pl doesn't match
test baz.pl doesn't match
0test_bar.pl doesn't match
test_me.pl matches
test_me_too.txt doesn't match

Upvotes: 1

Andrew Clark
Andrew Clark

Reputation: 208545

You should be able to accomplish this by using a negative lookahead:

/test_(?!foo|bar|baz).*\.pl/

This will fail if foo, bar, or baz immediately follows test_.

Note that this could still match something like test_notfoo.pl, and would fail on test_fool.pl, if you do not want this behavior please clarify by adding some examples of what exactly should and should not match.

If you want to accept something like test_fool.pl or test_bart.pl, then you could change it to the following:

/test_(?!(foo|bar|baz)\.pl).*\.pl/

Upvotes: 6

Lumi
Lumi

Reputation: 15284

Hmm, I might have misunderstood your question. Anyway, maybe this is helpful ...


You would negate the match operator. For example:

perl -lwe "print for grep ! m/(lwp|archive).*\.pl/, glob q(*.pl)"
# Note you'd use single-quotes on Linux but double-quotes on Windows.
# Nothing to do with Perl, just different shells (bash vs cmd.exe).

The ! negates the match. The above is shorthand for:

perl -lwe "print for grep ! ($_ =~ m/(lwp|archive).*\.pl/), glob q(*.pl)"

Which can also be written using the negated match operator !~, as follows:

perl -lwe "print for grep $_ !~ m/(lwp|archive).*\.pl/, glob q(*.pl)"

In case you're wondering, the glob is simply used to get an input list of filenames as per your example. I just substituted another match pattern suitable for the files I had handy in a directory.

Upvotes: 0

Related Questions