从 perl -d 调试会话中按 CTRL-C 时清理 tmp 目录

Cleanup tmp dirs when hitting CTRL-C from perl -d debug session

对于脚本中需要的临时目录,我使用:

my $work_dir = File::Temp->newdir(TEMPLATE => "/tmp/work.$$.XXXX" ) or die "Cannot create tempdir directory $!";

我对 newdir() 的希望是得到承诺:

默认情况下,当对象超出范围时,目录将被删除。

才知道如果我按 CTRL-C,$work_dir 将不会 被删除。

所以我添加了信号:

use sigtrap qw(handler signal_handler normal-signals);

然后我简单地使用 (File::Path 'remove_tree');

sub signal_handler
{
    remove_tree $work_dir;
}

如果我在脚本运行时按 CTRL-C,这会有所帮助...

但是,它在使用调试器时不会清理,如果我在调试器外按 CTRL-C!如果我干净地退出(使用 q 命令)那么它 works/cleans 很好,只有当我 CTRL-C 退出调试会话时,那时候 $work_dir 没有被删除。

是否有可能以任何方式在 perl 调试会话中自动调用信号处理程序?

(或任何其他 "proper" 如何 use/install 信号处理程序的方式)

我喜欢使用 END 块。程序的任何干净退出,尤其是调试器的 'quit' 将触发 END 块,在我的情况下,删除我所有的测试数据。

因此,将清理代码放入 END 块中,让你的签名处理程序调用 exit() 而不是 remove_tree。

 END {
   remove_tree $work_dir;
 }

 sub signal_handler
 {
     exit();
 }

您的信号处理程序没有按照您的想法进行,因为将对象传递给 remove_tree 不起作用:

use strict;
use warnings;
use 5.010;

use File::Path qw(remove_tree);
use File::Temp;

my $tmpdir = File::Temp->newdir(CLEANUP => 0);
remove_tree $tmpdir;

say "$tmpdir still exists" if -d $tmpdir;

输出:

/tmp/lTfotn79RD still exists

在信号处理程序 中对 remove_tree 的调用似乎 在调试器外部 运行 时起作用,但它实际上没有做任何事情。 (您可以通过注释掉对 remove_tree 的调用并重新 运行 编写您的脚本来向自己证明这一点。)那么为什么目录会被删除?

如果信号处理程序没有 exitdie,则在信号被捕获之前从中断的地方继续执行。在您的情况下,信号处理程序完成后,程序只需 运行s 即可完成。当程序终止时,任何仍在范围内的对象都会通过调用它们的 DESTROY 方法来清理。 File::Temp->newdir returns 一个 File::Temp::Dir 对象;这个对象的 DESTROY 方法实际上是从文件系统中删除目录(它在幕后使用 rmtree)。

这在您中断调试器时不起作用;我不熟悉调试器的内部结构,但我猜它会保留对对象的引用,这样 DESTROY 就不会被调用,即使当您离开程序末尾时也是如此。如果此时再次 Ctrl+C ,对象永远不会被清理,临时目录也不会被清理。

我发现解决此问题的一种方法是在信号处理程序中显式 undefFile::Temp->newdir 返回的对象:

use strict;
use warnings;
use 5.010;

use File::Temp;

use sigtrap qw(handler cleanup normal-signals);

my $tmpdir = File::Temp->newdir;

sub cleanup {
    my ($sig) = @_;
    say "Caught signal SIG$sig";
    undef $tmpdir;
    exit 0;
}

这会导致 DESTROY 方法在 程序退出之前 被调用,因此您不依赖调试器进行清理。不过,这似乎有点像 hack;为什么不使用 q 优雅地退出调试器?

请注意,您还可以像这样将 $tmpdir 的字符串化版本传递给 remove_tree

remove_tree "$tmpdir";

但我不推荐这样做,因为文档 strongly cautions 反对依赖文件名:

For maximum security, endeavour always to avoid ever looking at, touching, or even imputing the existence of the filename. You do not know that that filename is connected to the same file as the handle you have, and attempts to check this can only trigger more race conditions. It's far more secure to use the filehandle alone and dispense with the filename altogether.