How to pass a replacement regular expression as a command line argument in a perl script

I am trying to write a simple perl script to apply this regular expression to the file name by the way, and it is difficult for me to pass the regular expression to the script as an argument.

What I would like to do is something like this:

> myscript 's/hi/bye/i' hi.h bye.h > 

I created this code

 #!/utils/bin/perl -w use strict; use warnings; my $n_args = $#ARGV + 1; my $regex = $ARGV[0]; for(my $i=1; $i<$n_args; $i++) { my $file = $ARGV[$i]; $file =~ $regex; print "OUTPUT: $file\n"; } 

I cannot use qr, because apparently it cannot be used when replacing regular expressions (although my source for this is a forum post, so I'm glad I turned out to be wrong).

I would prefer not to pass the two parts as separate lines and manually do the regular expression in the perl script.

Is it possible to pass a regular expression as an argument like this, and if so, what is the best way to do this?

+4
source share
4 answers

It seems to me that there are several ways to do this.

Ev i al Way:

As you basically send a regex expression, it can be evaluated to get the result. Like this:

 my @args = ('s/hi/bye/', 'hi.h'); my ($regex, @filenames) = @args; for my $file (@filenames) { eval("\$file =~ $regex"); print "OUTPUT: $file\n"; } 

Of course, after that some unpleasant surprises will be revealed to you. For example, consider passing this set of arguments:

 ... my @args = ('s/hi/bye/; print qq{MINE IS AN EVIL LAUGH!\n}', 'hi.h'); ... 

Yes, he will laugh at you the most.

illy.

Safe way:

 my ($regex_expr, @filenames) = @args; my ($substr, $replace) = $regex_expr =~ m#^s/((?:[^/]|\\/)+)/((?:[^/]|\\/)+)/#; for my $file (@filenames) { $file =~ s/$substr/$replace/; print "OUTPUT: $file\n"; } 

As you can see, we analyze the expression given to us in two parts, then use these parts to create a complete statement. Obviously, this approach is less flexible, but, of course, it is much safer.

The easiest way:

 my ($search, $replace, @filenames) = @args; for my $file (@filenames) { $file =~ s/$search/$replace/; print "OUTPUT: $file\n"; } 

Yes, that’s right - no regular expression exists! Here we decided to accept two arguments - "search pattern" and "string replacement" - instead of one. Will this make the script less flexible than the previous one? No, since we still needed to regularly analyze the regex expression. But now the user clearly understands all the data that is given to the team, which is usually an improvement. )

@args in both examples corresponds to the @ARGV array.

+8
source

s/a/b/i is an operator, not just a regular expression, so you need to use eval if you want it to be interpreted correctly.

 #!/usr/bin/env perl use warnings; use strict; my $regex = shift; my $sub = eval "sub { \$_[0] =~ $regex; }"; foreach my $file (@ARGV) { &$sub($file); print "OUTPUT: $file\n"; } 

The trick is that I replace this “bit of code” with a string to create Perl code that defines the anonymous routine $_[0] =~ s/a/b/i; (or any other code that you pass) and then using eval to compile this code and give me a link to the code that I can call from the loop.

 $ test.pl 's/foo/bar/' foo nicefood OUTPUT: bar OUTPUT: nicebard $ test.pl 'tr/o/e/' foo nicefood OUTPUT: fee OUTPUT: nicefeed 

This is more efficient than putting eval "\$file =~ $regex;" inside the loop, because then it will be compiled and evaluated at each iteration, and not just immediately.

Warning about eval - as raina77ow explains, you should avoid eval if you are 100% sure that you always get your input from a reliable source ...

+4
source

s/a/b/i not a regular expression. This is a regex plus replacement. If you are not using the eval string, make this work pretty complicated (consider s{a}<b>e , etc.).

+2
source

The problem is that you are trying to pass the perl statement when all you really need to pass is the arguments:

 myscript hi bye hi.h 

In the script:

 my ($find, $replace, @files) = @ARGV; ... $file =~ s/$find/$replace/i; 

Your code is a little awkward. This is all you need:

 use strict; use warnings; my ($find, $replace, @files) = @ARGV; for my $file (@files) { $file =~ s/$find/$replace/i; print "$file\n"; } 

Please note that this method allows you to use metacharacters in a regular expression, for example \w{2}foo? . It can be good and bad. For all characters to be entered literally (disable metacharacters), you can use \Q ... \E as follows:

 ... s/\Q$find\E/$replace/i; 
+2
source

Source: https://habr.com/ru/post/1434173/


All Articles