【问题标题】:How to get regex to work in a perl script?如何让正则表达式在 perl 脚本中工作?
【发布时间】:2022-10-14 00:25:34
【问题描述】:

我正在基于 Linux 的 Debian 环境(确切地说是 Proxmox 服务器)上工作,并且正在编写一个 perl 脚本。

我的问题是:我有一个文件夹,里面有一些文件,这个文件夹中的每个文件都有一个数字作为名称(例如:100、501、102 ...)。可能的最小数字是 100,最大数字没有限制。

我希望我的脚本只返回名称在 100 到 500 之间的文件。 所以,我写这个:

system(ls /the/path/to/my/files | grep -E "^[1-4][0-9]{2}|5[0]{2}");

我认为我的正则表达式和命令很好,因为当我在终端中输入它时,它是有效的。 但是一旦我执行我的脚本,我就会收到这些错误消息:

String found where operator expected at backupsrvproxmox.pl line 3, near "E "^[1-4][0-9]{2}|5[0]{2}""
    (Do you need to predeclare E?)
Unknown regexp modifier "/b" at backupsrvproxmox.pl line 3, at end of line
syntax error at backupsrvproxmox.pl line 3, near "E "^[1-4][0-9]{2}|5[0]{2}""
Execution of backupsrvproxmox.pl aborted due to compilation errors.

我也尝试过 egrep 但仍然无法正常工作。

我不明白为什么错误消息是关于 /b 修饰符的,因为我只使用整数而不使用字符串。

所以,任何帮助都会很好!

【问题讨论】:

  • 您的文件名是否以您要查找的数字开头?例如。 100_bears_in_the_woods.txt 还是 5000000_ways_to_cook_lizards.doc

标签: regex perl


【解决方案1】:

而不是通过system 使用系统工具可以很好地在你的程序中完成这一切

my @files = grep { 
    my ($n) = m{.*/([0-9]+)};                        #/ 
    defined $n and $n >= 100 and $n <= 500;
}
glob "/the/path/to/my/files/*"

这假设文件名中的数字位于文件名的开头,从问题中提取,因此文件名本身的子模式直接跟随/(右侧的“评论”#/ 只是为了关闭编辑器中错误和令人困惑的语法突出显示。)

由于语法错误,您尝试的命令不起作用,因为 system 接受一个字符串或字符串列表,而您给它一堆“裸词”,这让解释器感到困惑,发出复杂的错误消息(大多数时候 perl 的错误信息都是正确的)。

但是没有必要为语法细节而受苦,这可能会涉及到这一点,也不需要复杂和混乱(在引擎盖下)和低效的 shell 调用。


它还假设文件是不是在当前目录中——很明显,因为路径被传递给glob(而不仅仅是*用于当前目录中的文件),它返回带有路径的文件名,这就是我们需要.*/的原因在匹配文件名之前贪婪地到达最后一个/

但是如果我们在当前目录中,那将无法工作,因为文件名中没有/。为了包括这种可能性,需要修改正则表达式,例如

my ($n) = m{ (?: .*/ | ^) ([0-9]+)}x;  

这匹配以数字开头的文件名,或者在路径中的最后一个斜杠之后(使用.*/ 子模式)或在字符串的开头(使用^ 锚点)。

修饰符 /x 使其丢弃模式中的文字空格,因此我们可以自由使用它们(以及换行符和用于 cmets 的 #!),以使该混乱可能更具可读性。然后我还使用{} 作为分隔符,这样就不必在模式中转义/(并且对于// 以外的任何分隔符,我们必须有m)。

【讨论】:

  • glob 返回的字符串包含路径名,因此正则表达式不会按原样匹配。
  • @DaveMitchell 确实——非常感谢您的评论! (奇怪的是,我做了测试,但在一个单行中,并且文件在当前目录中,所以它从来没有轰炸过我。)修复,然后触发添加我认为适合这里的解释......
  • 也许您在这里使用了错误的工具。文件::基本名称?文件::找到?
【解决方案2】:

使用正则表达式尝试匹配一系列数字只是一种痛苦。这是 perl;无需使用外部程序来获取文件列表(通常在 shell 脚本中也是一个坏主意;请参阅Why you shouldn't parse the output of ls(1))!

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;

sub getfiles {
    my $directory = shift;
    opendir my $dir, $directory or die "Unable to open $directory: $!";
    my @files =
        grep { /^d+$/ && $_ >= 100 && $_ <= 500 } readdir $dir;
    closedir $dir;
    return @files;
}

my @files = getfiles '/the/path/to/my/files/';
say "@files";

或者使用有用的Path::Tiny 模块:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use Path::Tiny;

# Returns a list of Path::Tiny objects, not just names.
sub getfiles {
    my $dir = path($_[0]);
    return grep { $_ >= 100 && $_ <= 500 } $dir->children(qr/^d+$/);
}

my @files = getfiles '/the/path/to/my/files/';
say "@files";

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 2023-01-23
    相关资源
    最近更新 更多