【发布时间】:2016-08-02 04:21:23
【问题描述】:
我有一个长时间运行的程序,它使用File::Temp::tempdir 创建一个临时文件,有时通过^C 中断它。
以下程序打印它创建的临时目录的名称和其中的文件名。
#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];
my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";
`touch $dir/temp.txt`;
exit;
在 OS X 上,这会在 /var/folders 中创建一个目录
如果最后一行是exit; 或die;,则文件夹将被清理,其中的临时文件将被删除。
但是,如果我们将最后一行替换为sleep 20;,然后通过^C 中断perl 程序,则临时目录仍然存在。
% perl maketemp.pl
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
^C
% stat /var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
16777220 6589054 -rw-r--r-- 1 <name> staff 0 0 "Aug 1 20:46:27 2016" "Aug 1 20:46:27 2016" "Aug 1 20:46:27 2016" "Aug 1 20:46:27 2016" 4096 0 0
/var/folders/dr/cg4fl5m11vg3jfxny3ldfplc0000gn/T/ycilyLSFs6/temp.txt
%
使用只调用exit; 的信号处理程序确实会清理目录。例如
#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];
$SIG{INT} = sub { exit; };
my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";
`touch $dir/temp.txt`;
sleep 20;
与使用“微不足道”的信号处理程序一样
#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw[tempdir];
$SIG{INT} = sub { };
my $dir = tempdir(CLEANUP => 1);
print "$dir\n";
print "$dir/temp.txt\n";
`touch $dir/temp.txt`;
sleep 20;
我尝试查看源代码 (https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm) 以确定 tempdir 如何注册清理操作
这是退出处理程序的安装
https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L1716
调用_deferred_unlink
https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L948
它修改了全局哈希 %dirs_to_unlink 和 %files_to_unlink,但出于某种原因使用 pid $$ 作为密钥(可能是在 Perl 解释器分叉的情况下?不知道为什么这是必要的,因为删除目录看起来像这将是一个幂等操作。)
清理文件的实际逻辑在这里,在END 块中。
https://github.com/Perl-Toolchain-Gang/File-Temp/blob/master/lib/File/Temp.pm#L878
快速实验表明,当 perl 正常或异常退出时,END 块确实会运行。
sleep 20;
END {
print "5\n";
}
# does not print 5 when interrupted
并在这里运行
$SIG{INT} = sub {};
sleep 20;
END {
print "5\n";
}
# does print 5 when interrupted
那么...为什么END 块在 SIGINT 之后会被跳过,除非有一个信号处理程序,即使它看起来应该什么都不做?
【问题讨论】:
-
尝试用
sleep(20) or warn($!); print("done\n");替换sleep(20);
标签: perl