2017-07-22 31 views
0

我正在尝试创建一个Perl脚本,用于过滤STDIN上显示的数据,将所有出现的 一个字符串更改为另一个字符串,并输出所有输入行,并将其更改并保留为STDOUT。 FROMSTRING和TOSTRING可以是PERL兼容的正则表达式。我无法获得匹配的输出。带有替换的Perl过滤器

这里是什么,我想实现的例子。

echo "Today is Saturday" | f.pl 'a' '@' 

输出[email protected] is [email protected]@y

echo io | filter.pl '([aeiou])([aeiou])' '$2$1' 

输出oi。发现

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n" 
} 
exit 1; 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/; 
print $inLine 
} 
exit 0; 
+0

注 - 可以做'while(my $ inline = ){...}'。另外,'我的($ from,$ to)= @ARGV;' – zdim

+0

或'print s/$ from/$ to/r ;''用'/ r'修饰符[来自5.14](http:// perldoc。 perl.org/perl5140delta.html) – zdim

回答

0

三个错误:

; after error message 
exit 1; 
$inLine =~ s/$FROM/$TO/g; 

像:

#!/usr/bin/perl 
use strict; 
use warnings; 
if (@ARGV != 2){ 
     print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
     exit 1; 
} 
my $FROM = $ARGV[0]; 
my $TO = $ARGV[1]; 
my $inLine = ""; 
while (<STDIN>){ 
$inLine = $_; 
$inLine =~ s/$FROM/$TO/g; 
print $inLine 
} 
exit 0; 
+0

谢谢,那些只是我忽略的简单的语法错误。但是,当我尝试更复杂的东西,如filter.pl'([aeiou])([aeiou])''$ 2 $ 1''它没有正确完成。 – user8351474

+0

在块中的最后一条语句之后不需要';'。 – melpomene

+0

@ user8351474“$ 2 $ 1”是一个文字字符串(只是这四个字符),这就是它在正则表达式中的处理方式。所以你会得到'$ 2 $ 1'的结果。所以你需要赋予输入字符串在正则表达式中的含义(在这种情况下,变量'$ 2'和'$ 1'),这并不简单。查看[这篇文章](https://stackoverflow.com/a/45233407/4653379)为同一种问题。 – zdim

6

首先,一个操作s/.../.../的替换部分不是正则表达式;它像双引号字符串一样工作。

有几个与你的代码的问题。

  • exit 1;语句出现在主代码的中间,而不是在错误块。你可能想:

    if (@ARGV != 2) { 
        print STDERR "Usage: ./filter.pl FROMSTRING TOSTRING\n"; 
        exit 1; 
    } 
    
  • 你错过,如果你想多换人在同一行发生一g标志:

    $inLine =~ s/$FROM/$TO/g; 
    
  • 有没有必要预先声明$inLine;它只在一个块中使用。

  • 但也没有必要读行成$_只是将其复制到$inLine
  • 它通常使用$names_like_this变量和函数,而不是$namesLikeThis
  • 可以使用$0,而不是在错误信息硬编码的程序名。
  • exit 0;是在端冗余。

以下是更接近我怎么会写:

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

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    $line =~ s/$from/$to/g; 
    print $line; 
} 

这就是说,这些都不满足您与'$2$1'作为替换第二个例子。上面的代码不会做你想要的,因为$to是一个纯字符串。 Perl不会扫描它来寻找类似$1的东西并替换它们。

当您在代码中编写"foo $bar baz"时,它与'foo ' . $bar . ' baz'意思相同,但这仅适用于代码,即东西,字面上出现在您的源代码。在运行时不会重新扫描$bar的内容以扩展例如\n$quux。这也适用于$1和朋友,这只是普通变量。

那么你如何得到'$2$1'工作?

一种方法是混淆eval,但我不喜欢它,因为它是eval:如果你不是很小心,它会允许某人通过传递正确的替换字符串来执行任意代码“string ”。

不用eval就可以做到这一点,甚至可以使用例如Data::Munge::replace

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Munge qw(replace); 

if (@ARGV != 2) { 
    die "Usage: $0 FROMSTRING TOSTRING\n"; 
} 

my ($from, $to) = @ARGV; 

while (my $line = readline STDIN) { 
    print replace($line, $from, $to, 'g'); 
} 

replace作品像JavaScript的String#replace,从而扩展特殊$序列。

做手工也是可能的,但有点恼人,因为你基本上要像对待$to为模板,扩大手工所有$序列(通过其他正则表达式替换EG):

# untested 
$line =~ s{$from}{ 
    my @start = @-; 
    my @stop = @+; 
    (my $r = $to) =~ s{\$([0-9]+|\$)}{ 
     $1 eq '$' 
      ? '$' 
      : substr($from, $start[$1], $stop[$1] - $start[$1]) 
    }eg; 
    $r 
}eg; 

(这确实没有实现如${1}${2}等。这些都是作为练习留给读者桁组)。

此代码是足够烦人写(看看),我更喜欢使用一个模块一样Data::Munge的日是某种事情。

+0

不妨提及该模块。也许是使用'readline'而不是它的操作符'<>'的一个简单评论?我认为你更喜欢它在计算上的可读性和规范性意义(我不抱怨!),但它可能会让初学者感到困惑(就像在为什么这么做那样)? – zdim

+0

我从不使用'<' '>',因为它的特殊语法重载:'<$foo>'意味着'readline($ foo)','<${foo}>'和'<$ foo>'意味着'glob($ foo)'。在所有情况下只写出'readline'和'glob'就更清楚了(也毫不含糊)。 (奖金:非Perl程序员理解发生了什么。) – melpomene

+0

我这么认为,我完全同意这个观点。我指的是针对初学者的答案。但是,这不仅仅是一个简短的评论(这也不是必需的)。 – zdim