monk
monk

Reputation: 2115

Timeout the perl command

I am working on a old Solaris node which does not have coreutils(timeout) tool installed. I am trying to print output of three bash commands separated by "-" x 80 chars. One of the command is having bad rep of taking very long time and I need to timeout that command and print the remaining output. I am trying to use alarm shift but this does not help.

I need to keep the command as a one liner as I need to use this command in ansible's shell module. Can someone please help.

If I run the different CMDn commands on bash terminal, it returns following output:

This is
from CMDn

Where n is 0 to 3.

When I run following perl command it returns :

perl -sanle 'BEGIN{$sep=("-"x80)."\n"; \
       print "$sep $CMD0\n $sep $CMD1\n $sep $CMD2\n $sep $CMD2flt\n";exit}' \
       -- -CMD0="$(CMD0)" -CMD1="$(CMD1 -q all)" -CMD2="$(CMD2)" -CMD2flt="$(CMD3)"


--------------------------------------------------------------------------------
This is
from CMD0

--------------------------------------------------------------------------------
This is
from CMD1

--------------------------------------------------------------------------------
This is
from CMD2

--------------------------------------------------------------------------------
This is
from CMD3

Sometimes, CMD3 takes very long time and I need to timeout it and proceed with the print of CMD0, CMD1, CMD2. I tried this command but this does not help as it is not timing out and it will timeout for all the outputs. In this example I am using sleep 15 && CMD3 as CMD3 and timeout as 10 , but this does not helped. I followed this post Timeout command on Mac OS X?

perl -sanle 'BEGIN{alarm shift; $sep=("-"x80)."\n"; \
    print "$sep $CMD0\n $sep $CMD1\n $sep $CMD2\n $sep $CMD2flt\n";exit}' \ 
    -- -CMD0="$(CMD0)" -CMD1="$(CMD1 -q all)" -CMD2="$(CMD2)" -CMD2flt="$(sleep 15 && CMD3)" 10 

Expecting following output if (for example) CMD3 is timing out.

--------------------------------------------------------------------------------
This is
from CMD0

--------------------------------------------------------------------------------
This is
from CMD1

--------------------------------------------------------------------------------
This is
from CMD2

--------------------------------------------------------------------------------
Null or blank or  any intuitive text

Upvotes: 2

Views: 337

Answers (2)

monk
monk

Reputation: 2115

I used following command to run list of commands with timeout value, when timeout command is not available. This command will ignore the error and move to next command in the array if any command time out. remove eval{ local $SIG{ALRM} = sub { die "Timeout\n" }; to fail the command if immediate fail is desired.

perl -sanle 'BEGIN{$sep=("-"x80)."\n";@cmds = ("CMD0", "CMD1", "CMD2", "sleep 5 && CMD3");foreach $cmd (@cmds) {eval{ local $SIG{ALRM} = sub { die "Timeout\n" }; alarm $timeout ;print $sep, qx{$cmd};alarm 0}; if($@){print "$cmd timed out"}};exit 0}' -- -timeout=30

Following command will fail if timeout is hit.

perl -sanle 'BEGIN{$sep=("-"x80)."\n";@cmds = ("CMD0", "CMD1", "CMD2", "sleep 5 && CMD3");foreach $cmd (@cmds) {eval{  alarm $timeout ;print $sep, qx{$cmd};alarm 0}; if($@){print "$cmd timed out"}};exit 0}' -- -timeout=30

In above commands, CMDX is any Linux command. Inspired from https://stackoverflow.com/a/2563551/6309601

Upvotes: 2

mob
mob

Reputation: 118605

alarm won't help you here. The expression -CMD2flt="$(CMD3)" on the command line expands the output of CMD3 to a variable before the perl script is launched.

Fortunately, it is easy to whip up your own timeout script in perl.

#! perl
# timeout N cmd [arg1 [arg2 [...]]]
# execute  cmd [arg1 [...]]  and timeout after N seconds
$N = shift;
$pid = $$;
fork || exec($^X,"-e","sleep 1,kill(0,$pid) || exit for 1..$N;kill -9,$pid");
exec(@ARGV);

(The code inside the first exec call is the poor man's alarm. It will monitor the calling process [with process id $pid] for up to $N seconds and then send a kill signal. It is more effective than Perl's alarm, which may not be able to terminate all system calls or external commands).

Replace -CMD2flt=$(CMD3) with -CMD2filt=$($HOME/bin/timeout 30 CMD3) and you should be all set.

Upvotes: 4

Related Questions