2

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
TLP
  • 66,756
  • 10
  • 92
  • 149
monk
  • 1,953
  • 3
  • 21
  • 41
  • You don't say which Solaris version you are using? Perl is pretty consistent across different platforms, so this may be an option... https://stackoverflow.com/a/50045118/2836621 – Mark Setchell Sep 04 '20 at 21:59

2 Answers2

4

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.

mob
  • 117,087
  • 18
  • 149
  • 283
  • Due to some restrictions about the design , I cannot depend on creating the file(script) . I searched more on SO, found one of your solution which is dealing with similar issue. I tweaked it a bit : `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` – monk Sep 04 '20 at 23:21
  • used this as reference https://stackoverflow.com/a/2563551/6309601 – monk Sep 04 '20 at 23:22
  • Yes, another approach is to bring `CMD3` and its friends inside the Perl script (though again, `alarm` and `SIGALRM` are not always effective at interrupting an external command). – mob Sep 04 '20 at 23:27
  • If you agree , you may please add it to your answer for future help. Thanks for your kind help. – monk Sep 04 '20 at 23:30
2

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

monk
  • 1,953
  • 3
  • 21
  • 41
  • The `BEGIN` block is used for code that is meant to be run before the normal execution of the code. Not as a way to start the code. In your case, it seems to be put around ALL of the code, which means it is not really used at all. You are also using the `-l` and `-n` switch. `-l` tells Perl to handle line endings for you, which in your case only leads to you having double line endings `\n` between your prints. – TLP Sep 05 '20 at 07:33
  • `-n` tells Perl to put a `while(<>) { ... }` loop around the code (which is used to read STDIN or argument files), which in your case would lead your Perl command to seem to "hang" when used (since you do not give it any input, nor any arguments that can be used as file names). It may seem to you that when you use `-n` you have to use `BEGIN` to make your command do something, but it is wrong to use them both like this. You are also using the `-a` switch, which splits the input into `@F` when used with `-n` (or `-p`). This is entirely unused in your command. Remove these and see what happens. – TLP Sep 05 '20 at 07:36
  • You are also using `exit`, which may be something you think you need to do because the `-n` switch seems to hang your command. It is quite unnecessary if you remove the unused `-n`. – TLP Sep 05 '20 at 07:48