0

I have the following lines of text in some configuration file, need to parse that file using Perl, find the File: line and replace its content using some regular expression to add some text etc.

File: logs/${byYearMonth}.log

The problem I have is with the regular expression, because I would like to use ${byYearMonth}.log in it, as it's easy to read, can easily be quoted etc. But this looks like variable interpolation to Perl and I get the following compilation error for the following simplified reg exp:

... =~ s/...\Q${byYearMonth}.log\E.../.../m;

Global symbol "$byYearMonth" requires explicit package name (did you forget to declare "my $byYearMonth"?)

Of course I can rework the reg exp to not let Perl think it's a variable name, but the provided version above is the most easiest to read and search for in my opinion. Thinking and researching about the problem, I didn't find any solution which would allow me to keep the reg exp as is and prevent the Perl compilation error by only adding some flag or whatever.

So, is there any (easy) way to tell Perl that some plain and already quoted text is not to be interpolated to get rid of the compilation error?

I have the feeling I'm missing something really easy, so thanks for your hints! :-)

12
  • 1
    This looks rather a lot like XML. Is it? Commented Dec 14, 2017 at 13:18
  • 1
    @Thorsten: You really shouldn't remove useful information from your question to prevent people from identifying that you have taken the wrong approach. Regular expressions are the wrong tool for processing XML, and such a solution is liable to backfire on you and your company. Making wholesale changes to your question like that also makes nonsense of people's existing comments and solutions. Commented Dec 15, 2017 at 12:25
  • 1
    @Thorsten: So it is XML? If so then qr'...' is not the answer you should be looking for: it is an error-prone hack. The best answers correct misconceptions in the question as well as providing a working solution to the stated problem. Sobrique's answer should not be down voted just because you insist on using a faulty solution. Commented Dec 15, 2017 at 16:52
  • 4
    Note: This question is being discussed on meta. Commented Dec 15, 2017 at 21:56
  • 5
    Contrary to popular belief, using patterns on little, limited pieces of reasonably well-defined pieces of HTML is quick and easy. The stigma here is unnecessary and wrong. Sure, if you want to perform complicated tasks on potentially malformed HTML (note that XML is much easier to deal with than HTML) you're probably better off with a parser, but if you just need to do a simple text replacement, you probably don't need a parser and RegEx is OK. Commented Dec 15, 2017 at 22:08

3 Answers 3

2

The \Q and \E are for quoting regex meta characters. They have nothing to do with variable interpolation.

If you construct your pattern with qr'' with single quotes '', Perl will not interpolate variables.

use feature 'say';

my $foo = 1;
my $bar = qr/$foo/;
say $bar;

Output:

(?^:1)

But with qr'':

my $foo = 1;
my $bar = qr'$foo';
say $bar;

Output:

(?^:$foo)
Sign up to request clarification or add additional context in comments.

5 Comments

Still this might cause an issue as $ is a special character for regex, and it is not escaped
@Pradeep combined with quotemeta or \Q and \E it should be fine.
@AlvaroJoao there's no difference. You can pass a list of imports to use after the module name. feature is a pragma, but that's kind of like a module with a lower case name really. The 'say' is just one scalar. If you do qw/say/ it will create a list of scalars with words that are automatically quoted, with one scalar in it. In list context, everything is a list in Perl. So essentially they are two ways of importing one thing. People often use qw// directly, because use Foo 'bar', 'baz' looks weird, and use Foo qw/bar baz/ is nicer to read.
My reason for accepting this answer seems to have "magically" disappeared: qr'...' is exactly what I was looking for and is even documented to prevent my kind of problem: "If "'" is used as the delimiter, no variable interpolation is done." perldoc.perl.org/perlop.html#Regexp-Quote-Like-Operators
Combining qr'...' and \Q...\E doesn't work: Unrecognized escape \E passed through in regex;[...] Using qr/\Q...\E/ with ... being the result of qr'' doesn't work as well. Therefore one either needs to escape $, {, ´} and . individually or use quotemeta right from the start. qr'...' isn't saving anything in the end, because quotemeta doesn't suffer from my problem and quoting individually prevents Perl from thinking of variable interpolation as well. So this answers my question by word, but was actually not was I hoped to find. :-)
1

I am going to call the other answers wrong, on the basis that manipulating what looks a lot like XML via regex is a bad idea.

And if it's actually not XML, then using a data format that looks like XML but isn't is an even worse idea.

So the answer is 'use a parser' (or alternatively, hit whoever generated the file with a rolled up copy of the XML spec).

Something like this will change the contents of the <File> element:

#!/usr/bin/perl
use strict;
use warnings;

use XML::Twig;

my $xml = XML::Twig -> new -> parsefile ( 'your_file.xml'); 

my $file_elt = $xml -> get_xpath ('//File',0);

print "Original value:", $file_elt -> text,"\n";
$file_elt -> set_text('some/other/path/${byWeek}.log');

$xml -> set_pretty_print ( 'indented' ); 
$xml -> print;

Note - get_xpath only finds the first instance of <File> anywhere in the tree. If you need to be more specific, you can either iterate or add additional qualifiers to the xpath.

So for example:

my $target_text = quotemeta '${byYearMonth}'; 
my $search_regex = qr/$target_text/; 

foreach my $file_elt ( $xml -> get_xpath('//File') ) { 
  if ( $file_elt -> text =~ /$search_regex/ ) { 
      ## set it to something else. 
  }
}

7 Comments

Down vote because the RegExp vs. XML-Parser discussion is off-topic and not relevant to my question, which is about variable interpolation only. Will change my text example to prevent that kind of discussion.
@Thorsten: Your downvote is highly inappropriate. Sobrique has shown you a correct way to process your data.
@Borodin My question is not related to XML processing, but his answer focusses on that and doesn't even provide the answer I was really looking for, qr'...'. He is addressing my question only by the quotemetaside note.
@Borodin Nope, I'm saying that I don't asked how to properly parse XML and therefore it doesn't make sense to explain how to properly parse XML, while addressing the concrete question only in a side note, if at all. Other answerers didn't care for XML or not, but focussed on the concrete question...
The reason I answered about parsing XML, is because your first question actually was about parsing XML. There are other ways to select fields within XML that regex cannot do - xpath is more fully featured. You were tackling an XY Problem. The fact that you've edited your question to remove that context and then down voted my answer afterwards is disappointing.
|
-1

Escape the pattern with quotemeta and then compile the regex. Something like this, it works...

my $s = q[<timestamp key="byYearMonth" datePattern="yyyy-MM" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <File>logs/${byYearMonth}.log</File>
    <Append>true</Append>

    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>%date %-5level %logger.%M: %msg%n</Pattern>
    </encoder>
</appender>];

my $pat = quotemeta('${byYearMonth}.log');
my $re = qr[$pat];

$s =~ s/$re/pop/g;

print $s;

1 Comment

This answer doesn't deserve all the down votes, quotemeta seems the only solution to handle escaping and non-variable-interpolation in one easy way. See my other comment: stackoverflow.com/questions/47809922/…

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.