Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to match an instance of a string but only the last instance if it exists. Here is my code:
use strict; use warnings; my $string = some data, echo DONE Upgrading packages... Cleaning upgr +ade files... Starting services... DONE'; if ($string =~ /(?!echo Done) Done/i) { print "match found\n"; }
The problem is that if the last DONE doesn't exist then a match is still seen and I need to exclude the 'echo DONE'. I suppose I could always remove any instance of 'echo DONE' and then try my match...but I was hoping to do it in my regex.

Thanks

Replies are listed 'Best First'.
Re: regex help needed
by kennethk (Abbot) on Jul 17, 2009 at 16:45 UTC
    Two quick possibilities come to mind, based upon your provided input:

    1. Since 'DONE' is the last element of your string, you could anchor to the end of the string with ^ (Regular Expressions), a la:

      $string =~ /DONE$/i;

    2. If you know the only undesirable DONE follows echo and will be the only one that follows echo, you could use a negative look behind assertion (Looking ahead and looking behind) to skip that case:

      $string =~ /(?<!echo\s)DONE/i;

      using the negative look behind worked...thanks!
Re: regex help needed
by jwkrahn (Abbot) on Jul 17, 2009 at 16:40 UTC

    It looks like you want something like:

    if ($string =~ /(?<!echo )Done$/i) { # update to look-behind instead o +f look-ahead print "match found\n"; }
      Thats better but DONE isn't always the last item in the string.
      That's not any different from /Done$/. (Originally jwkrahn had /(?!echo )done$/ - but he updated it after I posted my reply)
Re: regex help needed
by JavaFan (Canon) on Jul 17, 2009 at 17:07 UTC
    A way using negative lookbehind as been shown. Others are:
    /(?:^.{0,4}|[^e].{4}|[^c].{3}|[^h].{2}|[^o].|[^ ])done/is /(.....)done(??{$1 eq "echo " ? "(*FAIL)" : ""})|^.{0,4}done/is # Repl +ace (*FAIL) with (?!) pre-5.10
    Of course, if you know you only need to look at DONE at the end, you can use:
    !/echo done$/i && /done$/i