如何在 Perl 中捕获 ctrl+c 并关闭文件描述符
How to catch ctrl+c and close file descriptor in Perl
我有 Perl 脚本 - 简单的服务器。循环等待connect,写入新的光盘文件。当按下 ctrl + c
时,服务器应该停止。退出前,我想关闭所有文件描述符。这并不容易,因为终端显示错误。
是的,我读了这个线程:
How can I check if a filehandle is open in Perl?
但这对我不起作用。
这是我的主循环:
my $my_socket = new IO::Socket::INET(
LocalHost => $local_host,
LocalPort => $local_port,
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "Couldn't create: my_socket $!n " unless $my_socket;
print "Send files.. \n";
while(1) {
my $accepter = $my_socket->accept();
my $count = 0;
#print "$directory.$save_dir/$my_data";
$datetime = localtime();
$datetime =~ s/(?<=\w)\s(?=\w)/_/g;
open my $fh, '>', "$direc/$save_dir/$datetime"
or die "Couldn't open the file";
while(<$accepter>){
chomp;
#last if $count++ ==10;
#print($accepter);
say $fh $_;
}
$SIG{'INT'} = sub {
print "Caught One!\n";
close $fh;
}; #It gives me error. Close fd which is not opened.
}
#print "Received. End \n";
#close $fh;
close $my_socket;
信号处理程序中的代码关闭该文件句柄——并让循环继续。因此,下一次该文件句柄确实已关闭,因此您会收到警告。 (如果问题描述得当,我希望在下一次打印时首先出现问题,但可能有避免这种情况的原因。)
修复,简而言之 -- 在信号处理程序中设置一个标志,仅此而已。在代码中的合适位置检查它,如果已设置,则关闭文件并通过 last
.† 退出循环(或执行其他操作,视情况而定用于您的代码。)
不过,关于信号处理程序,我还想多说几句。 %SIG
是一个非常全球化的生物。通过设置 $SIG{INT}
,您已经为解释器中的所有代码更改了它。
为什么不使用 local $SIG{INT}
呢?然后它只在定义它的范围内被改变;参见 local。我会把这个定义拉到所有可能的循环之外。 (如果你真的想让它成为全球性的,就把它放在开头,这样它就响亮而清晰,而不是隐藏起来。)
所以像
SOME_SCOPE:
{
my $got_signal;
local $SIG{INT} = sub {
#say "Caught: @_";
$got_signal = 1;
};
while (1) {
...
open my $fh, '>', ... or die $!;
while (<$accepter>) {
...
if ($got_signal) {
close $fh;
# $got_signal = 0; # if used again (reset it)
last;
}
say $fh $_;
}
...
}
};
这仍然是一个草图,即使它在更简单的情况下也能正常工作。我必须假设一些事情,首先,您的代码中还有很多事情要做。如果包含完整的可运行示例可以帮助让我知道,我可以添加它。
† 不能在信号处理程序的子程序中执行 last(它不在循环内,即使它在代码中定义)。通常,至少可以说,在 sub 中使用 last
不是一个好主意,因为它会导致代码混乱和不透明。它还具有非常特殊的行为
last
cannot return a value from a block that typically returns a value, such as eval {}
, sub {}
, or do {}
. It will perform its flow control behavior, which precludes any return value.
(它也带有警告)在这种情况下,您不需要 return 一个值,但是(即使它有效)您绝对不想这样做 超出信号处理程序。
请注意,即使在问题中有链接,也可以使用 Scalar::Util::openhandle
检查文件句柄是否打开。这可以在这段代码中派上用场。
如果您希望服务器在按下 Ctrl-C 时真正退出,只需在中断处理程序中放置一个 exit
语句即可。这将在 Perl 退出时自动关闭所有文件句柄。
如果您希望读取循环在按下 Ctrl-C 时终止,但程序将继续执行,请执行此操作。但是我可能会建议 Ctrl-\ 作为发送 SIGQUIT 信号的更好选择。大多数人不希望从 Ctrl-C 中优雅地退出;他们希望立即停止。
our $doloop = 1;
$SIG{QUIT} = sub { $doloop = 0; };
while ($doloop) { ... }
$SIG{QUIT} = "IGNORE";
cleanup;
exit;
如果您需要在尝试关闭文件句柄之前检查文件句柄是否打开,只需使用 -e。对于其他类型的开放式测试,请参阅 perlfunc -X。
close $fh if -e $fh;
HTH
我有 Perl 脚本 - 简单的服务器。循环等待connect,写入新的光盘文件。当按下 ctrl + c
时,服务器应该停止。退出前,我想关闭所有文件描述符。这并不容易,因为终端显示错误。
是的,我读了这个线程:
How can I check if a filehandle is open in Perl?
但这对我不起作用。
这是我的主循环:
my $my_socket = new IO::Socket::INET(
LocalHost => $local_host,
LocalPort => $local_port,
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "Couldn't create: my_socket $!n " unless $my_socket;
print "Send files.. \n";
while(1) {
my $accepter = $my_socket->accept();
my $count = 0;
#print "$directory.$save_dir/$my_data";
$datetime = localtime();
$datetime =~ s/(?<=\w)\s(?=\w)/_/g;
open my $fh, '>', "$direc/$save_dir/$datetime"
or die "Couldn't open the file";
while(<$accepter>){
chomp;
#last if $count++ ==10;
#print($accepter);
say $fh $_;
}
$SIG{'INT'} = sub {
print "Caught One!\n";
close $fh;
}; #It gives me error. Close fd which is not opened.
}
#print "Received. End \n";
#close $fh;
close $my_socket;
信号处理程序中的代码关闭该文件句柄——并让循环继续。因此,下一次该文件句柄确实已关闭,因此您会收到警告。 (如果问题描述得当,我希望在下一次打印时首先出现问题,但可能有避免这种情况的原因。)
修复,简而言之 -- 在信号处理程序中设置一个标志,仅此而已。在代码中的合适位置检查它,如果已设置,则关闭文件并通过 last
.† 退出循环(或执行其他操作,视情况而定用于您的代码。)
不过,关于信号处理程序,我还想多说几句。 %SIG
是一个非常全球化的生物。通过设置 $SIG{INT}
,您已经为解释器中的所有代码更改了它。
为什么不使用 local $SIG{INT}
呢?然后它只在定义它的范围内被改变;参见 local。我会把这个定义拉到所有可能的循环之外。 (如果你真的想让它成为全球性的,就把它放在开头,这样它就响亮而清晰,而不是隐藏起来。)
所以像
SOME_SCOPE:
{
my $got_signal;
local $SIG{INT} = sub {
#say "Caught: @_";
$got_signal = 1;
};
while (1) {
...
open my $fh, '>', ... or die $!;
while (<$accepter>) {
...
if ($got_signal) {
close $fh;
# $got_signal = 0; # if used again (reset it)
last;
}
say $fh $_;
}
...
}
};
这仍然是一个草图,即使它在更简单的情况下也能正常工作。我必须假设一些事情,首先,您的代码中还有很多事情要做。如果包含完整的可运行示例可以帮助让我知道,我可以添加它。
† 不能在信号处理程序的子程序中执行 last(它不在循环内,即使它在代码中定义)。通常,至少可以说,在 sub 中使用 last
不是一个好主意,因为它会导致代码混乱和不透明。它还具有非常特殊的行为
last
cannot return a value from a block that typically returns a value, such aseval {}
,sub {}
, ordo {}
. It will perform its flow control behavior, which precludes any return value.
(它也带有警告)在这种情况下,您不需要 return 一个值,但是(即使它有效)您绝对不想这样做 超出信号处理程序。
请注意,即使在问题中有链接,也可以使用 Scalar::Util::openhandle
检查文件句柄是否打开。这可以在这段代码中派上用场。
如果您希望服务器在按下 Ctrl-C 时真正退出,只需在中断处理程序中放置一个 exit
语句即可。这将在 Perl 退出时自动关闭所有文件句柄。
如果您希望读取循环在按下 Ctrl-C 时终止,但程序将继续执行,请执行此操作。但是我可能会建议 Ctrl-\ 作为发送 SIGQUIT 信号的更好选择。大多数人不希望从 Ctrl-C 中优雅地退出;他们希望立即停止。
our $doloop = 1;
$SIG{QUIT} = sub { $doloop = 0; };
while ($doloop) { ... }
$SIG{QUIT} = "IGNORE";
cleanup;
exit;
如果您需要在尝试关闭文件句柄之前检查文件句柄是否打开,只需使用 -e。对于其他类型的开放式测试,请参阅 perlfunc -X。
close $fh if -e $fh;
HTH