如何在 Perl 中终止带有警报的系统命令?

发布于 2024-08-27 01:43:48 字数 1071 浏览 9 评论 0 原文

我正在 Windows 上运行以下代码片段。服务器从客户端读取数据后开始持续监听。我想在一段时间后终止这个命令。

如果我在 main.pl 中使用 alarm() 函数调用,然后它终止整个 Perl 程序(这里是 main.pl),所以我称之为 system 命令,将其放置在单独的 Perl 文件中 并使用 alarm.pl) >系统命令。

但通过这种方式,我既无法在原始 Perl 文件中也无法在调用的 Perl 文件中获取此 system() 调用的输出。

有人可以让我知道终止 system() 调用的方法或以我上面使用的方式获取输出吗?

main.pl

my @output = system("alarm.pl");
print"one iperf completed\n";

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

Alarm.pl

alarm 30;
my @output_1 = readpipe("adb shell cd /data/app; ./iperf -u -s -p 5001");

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

在这两种情况下,display.txt 始终为空。

I am running the below code snippet on Windows. The server starts listening continuously after reading from client. I want to terminate this command after a time period.

If I use alarm() function call within main.pl, then it terminates the whole Perl program (here main.pl), so I called this system command by placing it in a separate Perl file
and calling this Perl file (alarm.pl) in the original Perl File using the system command.

But in this way I was unable to take the output of this system() call neither in the original Perl File nor in called one Perl File.

Could anybody please let me know the way to terminate a system() call or take the output in that way I used above?

main.pl

my @output = system("alarm.pl");
print"one iperf completed\n";

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

alarm.pl

alarm 30;
my @output_1 = readpipe("adb shell cd /data/app; ./iperf -u -s -p 5001");

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

In both ways display.txt is always empty.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

诺曦 2024-09-03 01:43:48

这里有几个单独的问题。

首先,为了防止警报杀死您的脚本,您需要处理 ALRM 信号。请参阅警报文档。您不需要为此使用两个脚本。

其次,system 不捕获输出。如果您想这样做,您需要反引号变体之一或管道。 Stackoverflow 上已经有答案了。

第三,如果 alarm.pldisplay.txt 中放入任何内容,则当您以写入方式重新打开文件时,您会在 main.pl 中丢弃它模式。您只需在一处创建该文件。当你去掉额外的脚本时,你就不会遇到这个问题了。

我最近在alarmsystem方面遇到了一些问题,但是切换到 IPC::System::Simple 修复了这个问题。

祝你好运, :)

There are a few separate issues here.

First, to keep the alarm from killing your script, you need to handle the ALRM signal. See the alarm documentation. You shouldn't need two scripts for this.

Second, system doesn't capture output. You need one of the backtick variants or a pipe if you want to do that. There are answers for that on Stackoverflow already.

Third, if alarm.pl puts anything in display.txt, you discard it in main.pl when you re-open the file in write mode. You only need to create the file in one place. When you get rid of the extra script, you won't have this problem.

I recently had some problems with alarm and system, but switching to IPC::System::Simple fixed that.

Good luck, :)

为你鎻心 2024-09-03 01:43:48

我到底在想什么?您不需要后台进程来执行此任务。您只需按照 perldoc -f Alarm 函数并将时间敏感的代码包装在 eval 块中。

my $command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
my @output;
eval {
    local $SIG{ALRM} = sub { die "Timeout\n" };
    alarm 30;
    @output = `$command`;
    alarm 0;
};
if ($@) {
    warn "$command timed out.\n";
} else {
    print "$command successful. Output was:\n", @output;
}

eval 块内,您可以以常规方式捕获输出(使用反引号或 qx()readpipe)。但如果调用超时,则不会有任何输出。

如果您不需要输出(或者不介意一起破解某些进程间通信),一个几乎白痴的替代方案是设置警报并在子进程中运行 system 调用

$command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
if (($pid = fork()) == 0) {
    # child process
    $SIG{ALRM} = sub { die "Timeout\n" }; # handling SIGALRM in child is optional
    alarm 30;
    my $c = system($command);
    alarm 0;
    exit $c >> 8;  # if you want to capture the exit status
}
# parent
waitpid $pid, 0;

waitpid 将在孩子的 system 命令完成时返回,当孩子的闹钟响起并且杀死了孩子。 $? 将保存系统调用的退出代码,或者未处理的 SIGALRM 的其他代码(在我的系统上为 142),如果您的 SIGALRM 处理程序调用 die,则为 255。罢工>

What the hell was I thinking? You don't need a background process for this task. You just need to follow the example in the perldoc -f alarm function and wrap your time-sensitive code in an eval block.

my $command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
my @output;
eval {
    local $SIG{ALRM} = sub { die "Timeout\n" };
    alarm 30;
    @output = `$command`;
    alarm 0;
};
if ($@) {
    warn "$command timed out.\n";
} else {
    print "$command successful. Output was:\n", @output;
}

Inside the eval block, you can capture your output the regular way (with backticks or qx() or readpipe). Though if the call times out, there won't be any output.

If you don't need the output (or don't mind hacking some interprocess communication together), an almost idiot-proof alternative is to set the alarm and run the system call in a child process.

$command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
if (($pid = fork()) == 0) {
    # child process
    $SIG{ALRM} = sub { die "Timeout\n" }; # handling SIGALRM in child is optional
    alarm 30;
    my $c = system($command);
    alarm 0;
    exit $c >> 8;  # if you want to capture the exit status
}
# parent
waitpid $pid, 0;

waitpid will return when either the child's system command is finished, or when the child's alarm goes off and kills the child. $? will hold the exit code of the system call, or something else (142 on my system) for an unhandled SIGALRM or 255 if your SIGALRM handler calls die.

笑饮青盏花 2024-09-03 01:43:48

需要:

  • 运行系统命令并获取其输出
  • x 秒后系统命令超时
  • 杀死系统命令进程和所有子进程

我遇到了一个类似的问题, exec,我想出了这个解决方案。它是作为模拟“反引号”子例程实现的。

use Error qw(:try);

$SIG{ALRM} = sub {
    my $sig_name = shift;
    die "Timeout by signal [$sig_name]\n";
};

# example
my $command = "vmstat 1 1000000";
my $output = backtick( 
                 command => $command, 
                 timeout => 60, 
                 verbose => 0 
             );

sub backtick {

    my %arg = (
        command => undef,
        timeout => 900,
        verbose => 1,
        @_,
    );

    my @output;

    defined( my $pid = open( KID, "-|" ) )
        or die "Can't fork: $!\n";

    if ($pid) {

        # parent

        # print "parent: child pid [$pid]\n" if $arg{verbose};

        try {
            alarm( $arg{timeout} );
            while (<KID>) {
                chomp;
                push @output, $_;
            }

            alarm(0);
        }
        catch Error with {
            my $err = shift;
            print $err->{-text} . "\n";

            print "Killing child process [$pid] ...\n" if $arg{verbose};
            kill -9, $pid;
            print "Killed\n" if $arg{verbose};

            alarm(0);
        }
        finally {};
    }
    else {

        # child

        # set the child process to be a group leader, so that
        # kill -9 will kill it and all its descendents
        setpgrp( 0, 0 );

        # print "child: pid [$pid]\n" if $arg{verbose};
        exec $arg{command};
        exit;
    }

    wantarray ? @output : join( "\n", @output );
}

I run into a similar problem that requires:

  • run a system command and get its output
  • time out the system command after x seconds
  • kill the system command process and all child processes

After much reading about Perl IPC and manual fork & exec, I came out with this solution. It is implemented as a simulated 'backtick' subroutine.

use Error qw(:try);

$SIG{ALRM} = sub {
    my $sig_name = shift;
    die "Timeout by signal [$sig_name]\n";
};

# example
my $command = "vmstat 1 1000000";
my $output = backtick( 
                 command => $command, 
                 timeout => 60, 
                 verbose => 0 
             );

sub backtick {

    my %arg = (
        command => undef,
        timeout => 900,
        verbose => 1,
        @_,
    );

    my @output;

    defined( my $pid = open( KID, "-|" ) )
        or die "Can't fork: $!\n";

    if ($pid) {

        # parent

        # print "parent: child pid [$pid]\n" if $arg{verbose};

        try {
            alarm( $arg{timeout} );
            while (<KID>) {
                chomp;
                push @output, $_;
            }

            alarm(0);
        }
        catch Error with {
            my $err = shift;
            print $err->{-text} . "\n";

            print "Killing child process [$pid] ...\n" if $arg{verbose};
            kill -9, $pid;
            print "Killed\n" if $arg{verbose};

            alarm(0);
        }
        finally {};
    }
    else {

        # child

        # set the child process to be a group leader, so that
        # kill -9 will kill it and all its descendents
        setpgrp( 0, 0 );

        # print "child: pid [$pid]\n" if $arg{verbose};
        exec $arg{command};
        exit;
    }

    wantarray ? @output : join( "\n", @output );
}
放手` 2024-09-03 01:43:48

如果“timeout -n”在您的系统上已经很常见,则可以使用“timeout -n”来包装您的命令。

Might use "timeout -n " for wrapping your commands if thats already common on your system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文