簡體   English   中英

如何將替換正則表達式作為命令行參數傳遞給perl腳本

[英]How to pass a replacing regex as a command line argument to a perl script

我正在嘗試編寫一個簡單的perl腳本來將一個給定的正則表達式應用於文件名等,並且我無法將正則表達式作為參數傳遞給腳本。

我希望能做的是這樣的事情:

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

我已經制作了這段代碼

#!/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";
}

我不能使用qr,因為顯然它不能用於替換正則表達式(雖然我的來源是一個論壇帖子所以我很高興被證明是錯誤的)。

我寧願避免將這兩個部分作為單獨的字符串傳遞並在perl腳本中手動執行正則表達式。

是否可以將正則表達式作為這樣的參數傳遞,如果是這樣,最好的方法是什么?

我想,有不止一種方法可以做到這一點。

Ev 一世 方式:

由於您基本上發送了一個正則表達式,因此可以對其進行求值以獲得結果。 像這樣:

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

當然,按照這種方式將打開你一些非常討厭的驚喜。 例如,考慮傳遞這組參數:

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

是的,它會嘲笑你最ev 一種 意利。

安全方式:

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

如您所見,我們將給出的表達式解析為兩部分,然后使用這些部分構建一個完整的運算符。 顯然,這種方法不夠靈活,但當然,它更安全。

最簡單的方法:

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

是的,沒錯 - 根本沒有正則表達式解析! 這里發生的是我們決定采用兩個參數 - “搜索模式”和“替換字符串” - 而不是單個參數。 它會使我們的腳本不像前一個那么靈活嗎? 不,因為我們仍然需要或多或少地定期解析正則表達式。 但是現在用戶清楚地理解了給命令的所有數據,這通常是一個很大的改進。

兩個示例中的@args對應於@ARGV數組。

s/a/b/i是一個運算符,而不僅僅是一個正則表達式,所以如果你想要正確解釋它,你需要使用eval

#!/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";
}

這里的技巧是我將這個“代碼位”代入字符串以生成定義匿名子程序$_[0] =~ s/a/b/i; Perl代碼$_[0] =~ s/a/b/i; (或者你傳遞它的任何代碼),然后使用eval編譯該代碼並給我一個我可以在循環內調用的代碼引用。

$ test.pl 's/foo/bar/' foo nicefood
OUTPUT: bar
OUTPUT: nicebard

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

這比放置eval "\\$file =~ $regex;"更有效eval "\\$file =~ $regex;" 在循環內部,它將在每次迭代時進行編譯和評估,而不是僅在前面進行一次。

關於eval的警告 - 正如raina77ow的回答所解釋的那樣,除非你100%確定你總是從可信賴的來源獲得你的意見,否則你應該避免eval ...

s/a/b/i不是正則表達式。 這是一個正則表達式加替換。 除非你使用字符串eval ,否則這項工作可能非常困難(考慮s{a}<b>e等等)。

麻煩的是,當你真正需要傳遞的是參數時,你試圖傳遞一個perl運算符:

myscript hi bye hi.h

在腳本中:

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

你的代碼有點笨重。 這就是你所需要的:

use strict;
use warnings;

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

請注意,這種方式允許您在正則表達式中使用元字符,例如\\w{2}foo? 這既是好事,也是壞事。 要使所有字符按字面順序排列(禁用元字符),您可以像這樣使用\\Q ... \\E

... s/\Q$find\E/$replace/i;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM