2013-05-21 57 views
1

我有一个包含具体情况如下文件:Perl的正则表达式提取

 
/var/example/12.1.1.0-gn/product 
/var/example/12.1.1.0-xn/product 
       . 
       . 
/var/example/13.1.1.0-gn/product 
/var/example/13.1.1.0-xn/product 

我想用上面的路径,并插入新的变量,即:

 
/var/example/12.1.1.0/12.1.1.0-gn/product 
/var/example/12.1.1.0/12.1.1.0-xn/product 
       . 
       . 
/var/example/13.1.1.0/13.1.1.0-gn/product 
/var/example/13.1.1.0/13.1.1.0-xn/product 

我已经写下面的脚本是:

其中$new_add代表了新的部分增加的部分。我试图通过正则表达式来推广该脚本。我是perl的新手,所以如果我在某个地方错了,请指导我。谢谢。

open (FH) or dir ("Could not open the file"); 
foreach $line (<FH>){ 
    ($a, $b, $c, $d, $e, $f) = split ('/', $line); 
     chomp ($line); 
     print "$a, $b, $c, $d $e $f\n"; 
     if ($e =~ m/^\d.\d.\d.\d-\d+/){ 
      $new_add = $e; 
      print "Match"; 
     } 
} 
+2

您可能需要摆脱句点符号,因为没有修饰过,它会匹配任何字符。 –

回答

3

也许以下将是有用的:

use strict; 
use warnings; 

while (<>) { 
    s!(/\d[^-]+)!$1$1!; 
    print; 
} 

用法:perl script.pl inFile [>outFile]

第二,可选的参数指示输出到文件。

或者作为oneliner:perl -p -ne 's!(/\d[^-]+)!$1$1!' inFile [>outFile]

输出你的数据集:

/var/example/12.1.1.0/12.1.1.0-gn/product 
/var/example/12.1.1.0/12.1.1.0-xn/product 
/var/example/13.1.1.0/13.1.1.0-gn/product 
/var/example/13.1.1.0/13.1.1.0-xn/product 
+0

很好打高尔夫球,但是正则表达式对我来说似乎太松了。如果在某个地方还有另一个数字怎么办?也就是说,这可能是最小的正则表达式,可以用于他提供的输入。尝试使用'-p'来消除最后一个'print' – bonsaiviking

+0

@perreal是的,但是你必须将'/'分隔开来才能找到它。这可以在整个生产线上工作,而不会先分裂。 – bonsaiviking

+0

@bonsaiviking - 感谢您提供'-p'建议。已更新oneliner。 – Kenosis

0
use strict; 
use warnings; 

while (my $line = <>){ 
    my (@v) = split ('/', $line); 
    print join(" ", @v), "\n"; 
    if (my ($new_add) = $v[-2] =~ m/([^-]*)/){ 
     print "Match $new_add\n"; 
    } 
} 
4

你的Perl风格是基于Perl的4,采用一些更好的做法会让你的Perl写生活变得更轻松。首先,快速的解决问题的方法:

#!/usr/bin/perl -np 
use strict; 
use warnings; 
s{/(\d+\.\d+\.\d+\.\d+)-}{/$1/$1-}; 

这将匹配您的4部分版本字符串,捕捉它,并使它在你的目录路径的另一个因素。现在,以解决您的脚本,并告诉你一些更好的Perl:

第一,永远总是总是use strict; use warnings;启动脚本。这将执行脚本,这是伟大的一些严格的解释,因为Perl通常会认为它知道你想要什么,并尽一切可能避免造成错误。最明显的事情,use strict;确实是力lexical scoping,这意味着你必须用my声明变量。

所以你的第一行(use strict; use warnings;后):

open (FH) or dir ("Could not open the file"); 

的Perl现在会抱怨一些事情。首先,文件句柄是变量!因此,我们需要声明它们像这样:my $fh。坚持小写变量名称;它更具可读性。 Perl中也并不喜欢裸字dir。我觉得你的意思die,这是一个关键字:

open my $fh or die "Could not open the file"; 

好了,我们消除了一些不必要的括号,拍行更具可读性。但是现在该文件永远无法打开。这是因为你没有提供文件名!有两种使用open的方法很多,但是对于大多数的目的,最好的是3个参数的形式。参数是:文件句柄,模式文件名。在这种情况下,我们需要从文件中读取,所以模式"<"

open my $fh, "<", "test.txt" or die "Could not open the file"; 

这将是指出,可以通过包括use autodie;在离开错误处理学习Perl的好时机脚本的顶部。现在你的脚本是这样的:

#!/usr/bin/perl 

use strict; 
use warnings; 
use autodie; 

open my $fh, "<", "test.txt"; 

foreach my $line (<$fh>){ 

现在,foreachfor的代名词,这是我比较喜欢,因为它可以节省一些打字。 (my),并且钻石操作员(<>)现在围绕我们的词法文件句柄$fh。不幸的是,这会将整个文件拖入内存,这可能会造成问题。如果我们用一个while环代替,则每行存储,处理和丢弃,因为我们通过循环:

while (my $line = <$fh>) { 
    ($a, $b, $c, $d, $e, $f) = split ('/', $line); 

现在看看这个!许多变量需要在词汇范围内。一种方法是对所有人使用单一的my声明:my ($a, $b, $c, $d, $e, $f)。更好的主意是注意到我们有一系列相似的项目。这可能会写得更好:

my @path = split '/', $line; 

在那里,那很好!现在我不知道你为什么决定chomp下一行;它没有任何意义,因为在此之后你不使用$line,所以我们将跳过它。下一行必须进行修改,以使用新的@path变量:

print join(", ", @path), "\n"; 

使用join意味着我们不必知道我们有多少元件分开行成。我们也看到(从这个输出中)@path的第四个元素(索引3)是我们想要匹配的版本字符串,但是正则表达式有点偏离。

if ($path[3] =~ m/^\d.\d.\d.\d-\d+/){ 

这是寻找的一系列任何字符分隔个位数,而随后更多的数字后“ - ”。你的例子显示其中的一些应该是多位数,我们应该匹配文字“。”。 (句点,句号)而不是正则表达式“。” (任何字符),最后一部分可以是字母(“xn”,“gn”等)。这里有一个正则表达式匹配:

if ($path[3] =~ m/^(\d+\.\d+\.\d+\.\d+)-../){ 

你会发现我们增加了+意味着“一个或多个”和\逃脱.字符。还有一件事,我们添加了分组圆括号()来捕获版本字符串,与字符串的其余部分分开,因为这就是您想要的目录名称。这种捕获将被存储在$1变量,所以下一行是现在:

my $new_add = $1; 

而且仅此而已。很明显,您需要完成更多的工作才能完成脚本,但希望我已经为您提供了一些工具,使您的Perl体验更好。如果你想要的只是一个快速解决方案,那就是顶端的方法。

如果你想继续使用Perl进行编程,我建议你写一本教Perl 5的书,最好是在过去的5到6年中编写的书。一个我强烈推荐Modern Perl,这也是可用for free online

+1

这是一个美丽和详细的答案。但是我必须指出,你不应该为我的$行('<$fh>') - 这创建了文件中所有行的列表,然后遍历它们。 'while(my $ line = <$fh>)'更合适,因为它使用''>'运算符作为迭代器。 chomp实际上很重要,但是应该在* split之前完成。 – amon

+0

@amon非常好的一点!我将编辑我的答案以反映这一点。我做了很多Perl编程已经有一段时间了,所以我太专注于保留原始脚本的流程。 – bonsaiviking

+0

@all非常感谢您的时间和回复。 – deep