1

我有一个文件,其中包含以下详细信息:

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

3 回答 3

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;做法是强制词法作用域,这意味着您必须使用my.

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

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

Perl 现在会抱怨一些事情。首先,文件句柄是变量!所以我们需要像这样声明它们my $fh:坚持使用小写的变量名;它更具可读性。Perl 也不喜欢那个 bareword 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;在脚本顶部包含错误处理的好时机。现在您的脚本如下所示:

#!/usr/bin/perl

use strict; 
use warnings;
use autodie;

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

foreach my $line (<$fh>){

现在,foreach是 的同义词for,我更喜欢它,因为它节省了一些输入。$line被声明为词法(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意味着我们不必知道我们将线分成多少个元素。我们还看到(从这个输出)的第四个元素(索引 3)@path是我们想要匹配的版本字符串的那个,但是正则表达式有点偏离。

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,它也可以在线免费获得。

于 2013-05-21T02:47:18.633 回答
3

也许以下内容会有所帮助:

use strict;
use warnings;

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

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

第二个可选参数将输出定向到文件。

或作为单行者: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
于 2013-05-21T02:25:01.770 回答
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";
    }   
}
于 2013-05-21T01:58:08.953 回答