dideod.yang has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks. I wonder how to stop command and keep operating script??. I have many files that I want to search for 5 seconds. Below script,I use alarm but I can not keep my script because of die. If I erase die there is no reason to use alarm.. right? please help me
my $timeout = 5; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $timeout; my @file_list = `ls user/test/`; alarm 0; }; if ($@) { die unless $@ eq "alarm\n"; # propagate unexpected errors # timed out } else { # didn't } # I want to keep go scrip after stop alarm my $file; my @file_list; foreach $file(@file_list){ print "$file\n"; }

Replies are listed 'Best First'.
Re: how to stop command
by hippo (Archbishop) on Jul 22, 2018 at 10:08 UTC
    my $file; my @file_list; foreach $file(@file_list){ print "$file\n"; }

    This will achieve nothing, ever. @file_list is empty so the loop will not be entered. Is this the source of your confusion? If so, declare @file_list before the eval instead and then don't re-declare it inside.

      Hi :) you mean below script?
      my $timeout = 5; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $timeout; my @file_list = `ls user/test/`; my $file; my @file_list; foreach $file(@file_list){ print "$file\n"; } alarm 0; }; if ($@) { die unless $@ eq "alarm\n"; # propagate unexpected errors # timed out } else { # didn't }

        Not quite. I mean more like this:

        my $timeout = 5; my @file_list; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $timeout; @file_list = `ls user/test/`; alarm 0; }; if ($@) { die $@ unless $@ eq "alarm\n"; # propagate unexpected errors # timed out print "Have incomplete file list\n"; } else { print "Have complete file list\n"; } foreach my $file (@file_list) { print "$file\n"; }
Re: how to stop command
by haukex (Archbishop) on Jul 22, 2018 at 21:42 UTC

    Note that the pattern eval { ... }; if ($@) { ... } suffers from issues, and this pattern is better: eval { ...; 1 } or { ... }; - or just use a module like Try::Tiny.

    Other than the scoping issue of @file_list that hippo pointed out, I'm not sure I understand what problems you're having. I tried the following code on Linux and it times out the ls just fine:

    use warnings; use strict; use Data::Dumper; my @file_list; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm 5; chomp( @file_list = `sleep 10; ls /tmp` ); die "ls: \$?=$?" if $?; alarm 0; 1 } or do { die $@ unless $@ eq "alarm\n"; warn "Timed out"; }; print Dumper(\@file_list);

    Also, have a look at IPC::Run, it has timeout support, and support for several more powerful features. Here's an example that also shows Try::Tiny:

    use warnings; use strict; use Data::Dumper; use IPC::Run qw/ run timeout /; use Try::Tiny; my @file_list; try { run ['ls','/tmp'], \undef, \my $lines, timeout(5) or die "ls: \$?=$?"; @file_list = split /\n/, $lines; } catch { die $_ unless /^IPC::Run: timeout/; warn "Timed out"; }; print Dumper(\@file_list);

      I don't think this particular eval is likely to suffer from the issues you mentioned. There's no OO code inside the eval, so no object can be destroyed inside the eval.

        I don't think this particular eval is likely to suffer from the issues you mentioned.

        True, in this case it's probably fine. But I think it's still a good habit to get into - even seemingly innocent pieces of code can trigger the problematic behavior:

        eval { $foo = `some_bad_command` or die "blam" }; warn $@ if $@;

        won't print anything if $foo happened to be an object with the problematic DESTROY on a problematic version of Perl.

    A reply falls below the community's threshold of quality. You may see it by logging in.