如何在 Perl 中优化符号链接删除?

How can I optimize Symbolic Link Deletion in Perl?

提问人:tourist 提问时间:11/15/2023 更新时间:11/21/2023 访问量:105

问:

我正在开发一个 Perl 脚本来删除目录中的符号链接,我注意到当前使用 find 命令的方法需要相当长的时间。我正在寻找更有效的解决方案。

以下是当前的代码片段:

system("find '$dir' -type l -exec rm -f {} \\;");

我还尝试了使用取消链接和 glob 的替代方案:

unlink glob("$dir/*") if -e $dir;

然而,这两种方法似乎都有其缺点,我想知道是否有更优化的方法可以在 Perl 中实现符号链接删除。

  1. 是否有任何特定的优化可以应用于 find 命令?
  2. 有没有更有效的纯 Perl 解决方案来删除目录中的符号链接?
  3. 是否有任何专门从事目录遍历和链接操作的 Perl 模块可以提高性能?

任何关于优化删除过程的见解或建议将不胜感激。谢谢!

其他信息:

  • 目录 ($dir) 通常包含大量符号链接。
  • 我愿意使用 Perl 模块或可能提供更好性能的替代方法。
  • 性能基准或示例将特别有用。
perl 文件-io 符号链接 perl-module 目录遍历

评论

3赞 Steffen Ullrich 11/15/2023
find ... -type l -print0 | xargs rm -0f可能会更快,因为它不会为每个文件生成一个新的实例,而是使用单个实例一次删除多个文件。您还可以将 File::Find 而不是 shell 与 perl 函数一起使用,并且根本不生成任何外部进程。rmrmfind-lunlink
3赞 mob 11/15/2023
unlink ... if -e $dir肯定有其缺点。你是说吗?if -l $dir

答:

4赞 Timur Shtatland 11/15/2023 #1

使用 代替 传递给尽可能多的参数,而不是为每个文件执行一次:+;rm -f

system("find '$dir' -type l -exec rm -f {} \\+");

如果您知道需要在树中走多深,请使用,例如,这仅搜索顶层:-maxdepth M -mindepth N

system("find '$dir' -maxdepth 1 -mindepth 1 -type l -exec rm -f {} \\+");

评论

1赞 ikegami 11/15/2023
请注意,这是一个 GNU 扩展+
1赞 Shawn 11/15/2023
+这些天是 POSIX。查看 pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html
1赞 Shawn 11/15/2023
现在使用代替 ,...这仍然不是标准的,但效率要高得多。-delete-exec rm ...
5赞 Shawn 11/15/2023 #2

无需将外部进程拖入其中。该任务可以纯粹在perl中使用File::Find遍历目录树来完成:findrm

#!/usr/bin/env perl
use warnings;
use strict;
use File::Find;

my $dir = ...; # Fill in the blanks

find(sub { unlink $_ if -l $_ }, $dir);

评论

2赞 ikegami 11/15/2023
回复“任务可以纯粹在perl中完成”,当然,但问题是要更快地完成它。
0赞 tourist 11/22/2023
@Shawn这是最快的方法吗?
0赞 Shawn 11/22/2023
@tourist,您花在担心处理目录树的任何选项很久以前就会完成运行。
3赞 zdim 11/15/2023 #3

要清理单个目录,因此不是递归的,它只需要类似

-l && unlink for glob "$dir/*";

这将是如此之快,以至于性能有点难以衡量。您删除了多少个文件,多久删除一次?

上述后缀循环使用的一个明显缺点是无法正常检查错误。这很重要,尤其是在删除大量文件时,所以最好把它写出来for

for my $file (glob "$dir/*") {
    if (-l $file) { 
        unlink $file or warn "Error unlinking $file: $!"
    }
}

这样做确实会“影响”性能,但影响程度最低。


我想知道......该目录中总共有多少个条目(文件、目录、链接等)?如果数量过多,例如数十万,那么这本身可能会减慢爬行的遍历速度。扫描较大的目录当然需要更多时间,但如果条目数量变得非常多,系统可能会被击倒,可以这么说;shell 中的基本列表可能需要 10-20 分钟。

有一次,我发现如果这个数字真的过多,那么系统(等)受到的影响远远超过程序。如果这个观察结果普遍成立,或者至少在你的系统上也成立,那么在Perl中这样做会更好。lsfind

如果这真的是问题所在 - 文件数量过多 - 那么我想到两个选项。

File::Glob 库通过其bsd_glob提供了不对文件列表进行排序的方法

use File::Glob qw(:bsd_glob);

for my $file ( bsd_glob("$dir/*", GLOB_NOSORT) ) { 
    ...
}

如果确实有很多文件,这应该会有所帮助。感谢 Shawn 的评论。

另一种可能性是避免一次构建完整的文件列表,这是通过在列表上下文中使用来完成的,就像在循环中一样。在标量上下文中迭代,一次获取一个文件名,如果您真的有 200 万个文件或类似文件,这值得一试globforglob

while ( my $file = glob "$dir/*" ) { 
    if (-l $file) { 
        unlink $file or warn "Error unlinking $file: $!"
    }
}

试着把这些都计时,并澄清这有多慢,以及你有多少文件和链接。

评论

0赞 Shawn 11/15/2023
bsd_glob() 与该选项一起使用对于此方法可能很有用。GLOB_NOSORT
0赞 zdim 11/16/2023
@Shawn 确实,这是一个很好的提醒。添加,谢谢
0赞 tourist 11/21/2023
@zdim目录包含大约 50K 个文件(包括子目录),我需要递归搜索所有目录以获取符号链接。
0赞 zdim 11/22/2023
@tourist“我需要递归搜索”——啊,那不是这样。我不知道你需要递归地使用它,因为问题尝试了 ,这显然不做递归。glob
0赞 tourist 11/22/2023
@zdim @Shawn我正在尝试,但只是想知道它是否最有效。任何输入?find(sub { unlink $_ or warn "WARNING: Failed to unlink $_: $!\n" if -l $_ }, $dir);