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

Hello and thanks for taking the time to look at my issue.

I have a script that I'm working on that handles setup of new servers, via user input. The user enters server information which is then stored in a hash. The hash keys all start with 'node'.

The issue is that the hash entries are stored as node0-9wwn0, etc.

Sample:

Key: node0 Value: servername0 Key: node0wwn0 Value: servername0wwn0 ...
What I need is to pull JUST the node key (which is variable based on how many servers are entered...could be node0-node199).

If I regex match on node0-9 inside the loop it pulls the last hash entry (node0wwn0 in this case), and all I need is 'node0' and it's corresponding key. After several days of searching and getting nowhere with substr and regexes I decided to throw myself on the mercy of the Monks.

foreach my $key (sort keys %$ref) { if ($key ne "Server") { print $key .":" . " " . $ref->{$key} . "\n"; } ### Search the hash for cluster nodes. ### If they exist, perform operations ### Regex looks for the node.* keys, which indicates a cluster node. ### If there's no cluster, there won't be any node.* keys. if ($key =~ m/node.*$/) { print "We have a cluster! Here are the entries: " .$key. ":" . $ref-> +{$key}. "\n"; } if ($key =~ m/node[0-9]/) { print $key . ":" . $ref->{$key}. "\n"; } }
As you can see from above regex, that matches anything on that line with node0-9, when all I want to pull out of the hash is node0-9...my problem is that the key is variable, as I said, so I can't hard code it. Can someone point me in the right direction? Thank you much!

Replies are listed 'Best First'.
Re: Partial string match -- with a twist.
by SuicideJunkie (Vicar) on Jul 20, 2012 at 13:28 UTC

    See: Capture groups

    Basically, just slap some brackets around your node[0-9], and that portion of the string will be saved into the $1 variable for you to use later.

Re: Partial string match -- with a twist.
by Anonymous Monk on Jul 20, 2012 at 13:27 UTC
    What you want to match is: /(node[0-9]*)/

    If you match on .* that means "everything else" and that's exactly what you got.
      Revised code:

      if ($key =~ m/(node[0-9]*)/) { if ($key = $1) { print "You entered " .$key. ":" . $ref->{$key}. "\n"; } }
      That did the trick. Amazing how something so simple can be so easily missed. Thank you both very much.

        This form of matching is more common:
        if ($key =~ m/(node[0-9]*)/) { print "You entered " .$1. ":" . $ref->{$1}. "\n"; }
        Sorry if my advice was wrong.
Re: Partial string match -- with a twist.
by AnomalousMonk (Archbishop) on Jul 20, 2012 at 15:13 UTC
    What I need is to pull JUST the node key (which is variable based on how many servers are entered...could be node0-node199).   [emphases added]

    From the quote from the OP and from the examples therein, I get the notion that the pattern is best described as "the literal 'node' at the start of a string, immediately followed by 1 to 3 (and no more) decimal digits". The regex  /(node[0-9]*)/ matches  'node' anywhere, followed by any number (including zero) of digits. IMHO, a better (because more specific) regex would be
        m{ \A (node \d{1,3}) (?! \d) }xms
    (which has not been tested).

Re: Partial string match -- with a twist.
by Anonymous Monk on Jul 20, 2012 at 18:24 UTC
    A potentially better version would have been:

    /^(node[0-9]*)/

    ... notice underlined character ...

    ... which would force the string node to be considered only if it was anchored to the left-margin of the string, not anywhere in the string.

    It is also sometimes necessary to specify that a regular expression should not be "greedy." Normally, a regex will seek the longest substring that matches the pattern ("greedy") when what you actually want is the shortest one.