2

I have two variables coming from some user inputs. One is a string that needs to be checked and other one is a regular expression as below.

Following code doesn't work.

my $pattern = "/^current.*$/";
my $name = "currentStateVector";

if($name =~ $pattern) {
    print "matches \n";
} else {
    print "doesn't match \n";
}

And following does.

if($name =~ /^current.*$/) {
    print "matches \n";
} else {
    print "doesn't match \n";
}

What's the reason for this. I've the regular expression stored in a variable. Is there another way to store this variable or modify it?

1
  • The big thing is that // are quote-like-operators parsed by the language. So /^current.*$/ is parsed as a regex object ^current.*$ where as "/^current.*$/" is parsed as the string /^current.*$/ not a regex object. Commented Nov 1, 2019 at 21:06

2 Answers 2

5

The double-quotes that you use interpolate -- they first evaluate what's inside them (variables, escapes, etc) and return a string built with evaluations' results and remaining literals. See Gory details of parsing quoting constructs for an illuminating discussion, with lots of detail.

And your example string happens to have a $/ there, which is one of Perl's global variables (see perlvar) so $pattern is different than expected; print it to see. (In this case the / is erroneous as discussed below but the point stands.)

Instead, either use single quotes to avoid interpretation of characters like $ and \ (etc) so that they are used in regex as such

my $pattern = q(^current.*$);

or, better, use the regex-specific qr operator

my $pattern = qr/^current.*$/;

which builds from its string a proper regex pattern (a special type of Perl value), and allows use of modifiers. In this case you need to escape characters that have a special meaning in regex if you want them to be treated as literals.

Note that there's no need for // for the regex, and they wouldn't be a part of the pattern anyway -- having them around the actual pattern is wrong.

Also, carefully consider all circumstances under which user input may end up being used.


It is brought up in a comment that users may submit a "pattern" with extra /'s. That'd be wrong, as mentioned above; only the pattern itself should be given (surrounded on the command-line by ', so that the shell doesn't interpret particular characters in it). More detail follows.

The /'s are clearly not meant as a part of the pattern, but are rather intended to come with the match operator, to delimit (quote) the regex pattern itself (in the larger expression) so that one can use string literals in the pattern. Or they are used for clarity, and/or to be able to specify global modifiers (even though those can be specified inside patterns as well).

But then if users still type them around the pattern the regex will use those characters as a part of the pattern and will try to match a leading /, etc; it will fail, quietly. Make sure that users know that they need to give a pattern alone, with no delimiters.

If this is likely to be a problem I'd check for delimiters and if found carry on with a "loud" (clear) warning. What makes this tricky is the fact that a pattern starting and ending with a slash is legitimate -- it is possible, if somewhat unlikely, that a user may want actual /'s in their pattern. So you can only ask, or raise a warning, not abort.

Note that with a pattern given in a variable, or with an expression yielding a pattern at runtime, the explicit match operator and delimiters aren't needed for matching; the variable or the expression's return is taken as a search pattern and used for matching. See The basics (perlre) and Binding Operators (perlop).

So you can do simply $name =~ $pattern. Of course $name =~ /$pattern/ is fine as well, where you can then give global modifiers after the closing /

Sign up to request clarification or add additional context in comments.

10 Comments

this regex pattern coming from the user input, like set_parameter regex "/^current.*$/",
@ZamanX90 Users should not be putting those / around -- they aren't a part of the pattern. (If they do then your regex will then have that as a part of the pattern! It will try to match a slash in the beginning and end. So you may actually want to strip slashes in the script, if they surround input for the pattern?)
@ZamanX90 That's a good point that may be tricky, since you don't want "delimiters" to be submitted around a pattern -- while one may actually want surrounding slashes as a part of a pattern! Updated the answer with a comment.
Users should be writing a pattern on the command line just as they would with grep. grep -P '^ *current(?:foo|bar)?$' or script.pl '^ *current(?:foo|bar)?$' 'string'
@lordadmira Well, yes, that's what I say in the answer as well ("...they need to supply a pattern alone...") -- but the question is, what if/when they don't? If the program simply takes input and the user gave it with /'s then the program will happily use the whole thing as a pattern and so the regex will look for a leading / and just quietly fail. So the program should check for (wrong) delimiters, but then again, the /s may also be legit; thus the conundrum. That's what I comment on in the last paragraphs.
|
2

The slashes are part of the matching operator m//, not part of the regex.

When I populate the regex from user input

my $pattern = shift;

and run the script as

58663971.pl '^current.*$'

it matches.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.