J'ai une application appelée A qui reçoit une entrée n et s'exécute pendant un certain temps avant de renvoyer la réponse.

Je voudrais exécuter A pour augmenter les tailles de n avec le script bash simple suivant:

#!/bin/sh

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

Cependant, j'ai aussi besoin de quelque chose de très important. Si un appel time prend trop de temps à s'exécuter, parce que l'entrée est grande, alors j'ai besoin de tout le script bash pour arrêter de fonctionner.

Y a-t-il un moyen facile de faire ça?

3
jsguy 21 avril 2017 à 05:43

3 réponses

Meilleure réponse

Les éléments suivants pourraient fonctionner:

#! /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                                                                                             

Sur mon système, cela produit:

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

Ce n'est pas la plus jolie sortie, mais elle s'arrête après la première ligne: les résultats de synchronisation sleep 3 et sleep 5 ne sont jamais affichés.

Notez que timeout est une commande GNU coreutils, et n'est pas installée partout (si elle peut être installée du tout sur votre système). L'état de sortie peut également différer: 124 est la valeur par défaut, mais il est probablement préférable de tester $? != 0.

4
21 avril 2017 à 04:45
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);
}
0
user31264 21 avril 2017 à 03:50

Une approche consiste à démarrer à la fois sleep et votre processus en arrière-plan en même temps, et voir qui termine en premier.

Notez que ce qui suit suppose qu'aucun autre processus d'arrière-plan n'a été démarré sous le code actuel:

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

Bien sûr, vous pouvez utiliser une boucle:

v=10
while :; do
  abort_on_timeout "$timeout" ./A "$v"
  v=$((v + 10))
done
2
Charles Duffy 21 avril 2017 à 03:05