5

I have an application called A that receives an input n and runs for some time before returning the answer.

I would like to execute A for increasing sizes of n with the following simple bash script:

#!/bin/sh

time ./A 10
time ./A 20
time ./A 30
.
.
.

However I need something very important as well. If a time call takes too long to execute, because the input is large, then I need the entire bash script to stop running.

Is there an easy way of doing this?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
jsguy
  • 2,069
  • 1
  • 25
  • 36
  • 2
    I've edited to do so here, but in the future, please try to make your questions' titles specific to the individual question, so other folks can -- from looking at a title -- tell if a preexisting question and its answers will help them with their problem. – Charles Duffy Apr 21 '17 at 02:52
  • 2
    Possible duplicate of [Command line command to auto-kill a command after a certain amount of time](http://stackoverflow.com/questions/601543/command-line-command-to-auto-kill-a-command-after-a-certain-amount-of-time) –  Apr 21 '17 at 02:53
  • 1
    @Evert, it's a bit different here inasmuch as the intent is to abort the entire script, not just the individual command. Granted, not *very* different. – Charles Duffy Apr 21 '17 at 02:54
  • yes, it is very important to abort the entire script, because if `A` takes too long to run, I want another algorithm `B` to start running, which I have in another bash script. – jsguy Apr 21 '17 at 02:55
  • @CharlesDuffy Yes, I realised that; I'm being somewhat lazy. The way around it is to put each line in an if-statement, checking for the status of the actual command. `timeout` has a default exit status of 124 (on my machine), so you can then exit the complete script. –  Apr 21 '17 at 02:56
  • 1
    @Evert, that's **assuming** you have a `timeout` command; it's not POSIX-standard, or otherwise guaranteed to be available everywhere bash is. – Charles Duffy Apr 21 '17 at 03:00
  • @CharlesDuffy Correct, but it may be a very practical solution to the OPs problem. And may work for many others as well, so I certainly wouldn't dismiss such a solution. –  Apr 21 '17 at 03:04
  • *shrug*. I'm more inclined to point someone who needs a portable way to cause an individual command to time out at [BashFAQ #68](http://mywiki.wooledge.org/BashFAQ/068). That way they get pointed at `timeout`, but *also* have information on alternatives that work on platforms where it's not available. – Charles Duffy Apr 21 '17 at 03:09

3 Answers3

5

The following could work:

#! /bin/bash                                                                                   

timeout 2 time sleep 1                                                                         
if [[ $? == 124 ]]                                                                             
then                                                                                           
    exit;                                                                                      
fi                                                                                             

timeout 2 time sleep 3                                                                         
if [[ $? == 124 ]]                                                                             
then                                                                                           
    exit;                                                                                      
fi                                                                                             

timeout 2 time sleep 5                                                                         
if [[ $? == 124 ]]                                                                             
then                                                                                           
    exit;                                                                                      
fi                                                                                             

On my system, that produces:

0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 1696maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps

Not the prettiest output, but it stops after the first line: the results of timing sleep 3 and sleep 5 are never shown.

Note that timeout is a GNU coreutils command, and not installed everywhere (if it can be installed at all on your system). The exit status may also differ: 124 is the default, but it is probably better to test for $? != 0.

2

One approach is to start both sleep and your process in the background at the same time, and see who finishes first.

Note that the below assumes that no other background processes have been started below the code at hand:

timeout=30 # set this to the number of seconds you want

timed_run() {
  time "$@"
}

# use: abort_on_timeout timeout_in_seconds command_here
abort_on_timeout() {
  local sleep_pid cmd_pid retval
  local seconds=$1; shift
  wait # reap any preexisting background processes so they don't interfere
  sleep "$seconds" & sleep_pid=$!
  "$@" & cmd_pid=$!
  wait; retval=$?
  if kill -0 "$sleep_pid"; then
    # cmd finished before sleep
    kill "$sleep_pid"
    return "$retval"
  fi
  # sleep finished before cmd, so exit the whole script
  kill "$cmd"
  exit 1
}

abort_on_timeout "$timeout" ./A 10
abort_on_timeout "$timeout" ./A 20
abort_on_timeout "$timeout" ./A 30

Of course, you could use a loop:

v=10
while :; do
  abort_on_timeout "$timeout" ./A "$v"
  v=$((v + 10))
done
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
0
const char *env[] = {NULL};
const char *argv[] = {NULL};
const char *script = "my_script.sh";
const int time_interval = 10;

/* ... */

pid_t parent = getpid();
pid_t child = vfork();
if (child == -1) {
    printf("Pity! Cannot do vfork\n");
}
else if (child == 0) {
    execve(script, argv, env);
}
else {
    sleep(time_interval);
    kill(9, child);
}
user31264
  • 6,557
  • 3
  • 26
  • 40