Question

Cette réponse ligne de commande commande pour tuer automatiquement une commande après un certain laps de temps

1 propose un procédé en ligne à une commande de délai d'attente de longue durée à partir de la ligne de commande bash:

( /path/to/slow command with options ) & sleep 5 ; kill $!

Mais il est possible qu'une commande donnée « longue durée » peut terminer plus tôt que le délai d'attente. (Appelons un « typiquement de longue durée, mais, parfois rapide » commande, ou tlrbsf pour le plaisir.)

Cette approche astucieuse 1-liner a quelques problèmes. Premièrement, le sleep est pas conditionnel, de sorte que définit une limite inférieure indésirable sur le temps nécessaire pour que la séquence à la fin. Tenez compte 30 ou 2 m ou même de 5 m pour le sommeil, lorsque les tlrbsf la commande se termine en 2 secondes - très indésirables. En second lieu, le kill est inconditionnel, cette séquence va tenter de tuer un processus non-fonctionnement et jérémiades.

...

Est-il possible pour un délai d'attente typiquement de longue durée, mais, parfois rapide ( "tlrbsf" ) commande que

  • a une implémentation bash (l'autre question a déjà des réponses Perl et C)
  • se terminera à la première des deux: tlrbsf la fin du programme ou délai d'attente écoulé
  • ne sera pas tuer les processus non-existants / non en cours d'exécution (ou, le cas échéant: ne se plaignent au sujet d'un mauvais kill)
  • ne doit pas être un 1-liner
  • peut fonctionner sous Cygwin ou Linux

... et, pour les points de bonus, court le tlrbsf commande au premier plan et tout 'sommeil' ou d'un processus supplémentaire en arrière-plan, de sorte que le stdin / stdout / stderr du tlrbsf commande peut être redirigé, même comme si elle avait été exécuté directement?

Si oui, s'il vous plaît partager votre code. Sinon, s'il vous plaît expliquer pourquoi.

J'ai passé un certain temps à essayer de pirater l'exemple ci-dessus, mais je frappe la limite de mes compétences bash.

Était-ce utile?

La solution

Je pense que c'est exactement ce que vous demandez:

http://www.bashcookbook.com/bashinfo/ Source / bash 4.0 / examples / scripts / timeout3

#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.

# Hello Chet,
# please find attached a "little easier"  :-)  to comprehend
# time-out example.  If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>

scriptName="${0##*/}"

declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1

# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY

function printUsage() {
    cat <<EOF

Synopsis
    $scriptName [-t timeout] [-i interval] [-d delay] command
    Execute a command with a time-out.
    Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
    signal is blocked, then the subsequent SIGKILL (9) terminates it.

    -t timeout
        Number of seconds to wait for command completion.
        Default value: $DEFAULT_TIMEOUT seconds.

    -i interval
        Interval between checks if the process is still alive.
        Positive integer, default value: $DEFAULT_INTERVAL seconds.

    -d delay
        Delay between posting the SIGTERM signal and destroying the
        process by SIGKILL. Default value: $DEFAULT_DELAY seconds.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}

# Options.
while getopts ":t:i:d:" option; do
    case "$option" in
        t) timeout=$OPTARG ;;
        i) interval=$OPTARG ;;
        d) delay=$OPTARG ;;
        *) printUsage; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
    printUsage
    exit 1
fi

# kill -0 pid   Exit code indicates if a signal may be sent to $pid process.
(
    ((t = timeout))

    while ((t > 0)); do
        sleep $interval
        kill -0 $$ || exit 0
        ((t -= interval))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    kill -s SIGTERM $$ && kill -0 $$ || exit 0
    sleep $delay
    kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"

Autres conseils

Vous cherchez probablement la commande timeout dans coreutils. Comme il est une partie de coreutils, il est techniquement une solution C, mais il est encore coreutils. info timeout pour plus de détails. Voici un exemple:

timeout 5 /path/to/slow/command with options

Cette solution fonctionne indépendamment du mode moniteur bash. Vous pouvez utiliser le signal approprié de mettre fin à your_command

#!/bin/sh
( your_command ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

Le veilleur tue your_command après le délai donné; le script attend la tâche lente et se termine le veilleur. Notez que wait ne fonctionne pas avec les processus qui sont les enfants d'un autre shell.

Exemples:

  • your_command fonctionne plus de 2 secondes et a pris fin
  

your_command interrompu

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
  • your_command terminé avant que le délai d'attente (20 secondes)
  

your_command terminé

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi

Il vous allez:

timeout --signal=SIGINT 10 /path/to/slow command with options

vous pouvez modifier le SIGINT et 10 que vous le désirez;)

Je préfère "timelimit", qui a un paquet au moins dans debian.

http://devel.ringlet.net/sysutils/timelimit/

Il est un peu plus agréable que le coreutils « délai d'attente » parce qu'il imprime quelque chose en tuant le processus, et il envoie également SIGKILL après un certain temps par défaut.

Vous pouvez le faire entièrement bash 4.3 et au-dessus:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
  • Exemple: _timeout 5 longrunning_command args
  • Exemple: { _timeout 5 producer || echo KABOOM $?; } | consumer
  • Exemple: producer | { _timeout 5 consumer1; consumer2; }
  • Exemple: { while date; do sleep .3; done; } | _timeout 5 cat | less

  • a besoin Bash 4.3 pour wait -n

  • donne 137 si la commande a été tué, sinon la valeur de retour de la commande.
  • Travaux pour les tuyaux. (Vous n'avez pas besoin d'aller au premier plan ici!)
  • travaille avec des commandes shell internes ou des fonctions, aussi.
  • Runs dans un sous-shell, donc pas d'exportation variable dans le shell courant, désolé.

Si vous n'avez pas besoin du code de retour, cela peut être encore plus simple:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }

Notes:

  • Au sens strict, vous n'avez pas besoin ; en ; ), mais il fait chose plus cohérente à l'; } cas. Et le set +b peut probablement être laissé loin, aussi, mais il vaut mieux prévenir que guérir.

  • À l'exception des --forground (probablement), vous pouvez mettre en œuvre toutes les variantes timeout prend en charge. --preserve-status est un peu difficile, cependant. Ceci est laissé comme un exercice pour le lecteur;)

Cette recette peut être utilisé dans la coquille « naturellement » (aussi naturel que pour flock fd):

(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)

Cependant, comme expliqué ci-dessus, vous ne pouvez pas réexporter les variables d'environnement dans la englobante shell ainsi naturellement.

Edit:

Un exemple réel: Time out __git_ps1 au cas où il prend trop de temps (pour des choses comme SSHFS-Liens lents):

eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }

Edit2: Correctif. J'ai remarqué que exit 137 n'est pas nécessaire et fait _timeout peu fiables en même temps.

Edit3: git est un die-hard, il a besoin d'une double astuce pour travailler satisfyingly

.

Edit4: Vous avez oublié un _ dans la première _timeout pour l'exemple réel GIT

.

Voir aussi le script http://www.pixelbeat.org/scripts/timeout la fonctionnalité qui a été intégré dans les nouveaux coreutils

Kinda hacky, mais cela fonctionne. Ne fonctionne pas si vous avez d'autres processus de premier plan (s'il vous plaît aidez-moi à résoudre ce problème!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

En fait, je pense que vous pouvez inverser, répondant à vos critères 'bonus':

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa

délai d'attente est probablement la première approche d'essayer. Vous devrez peut-être une notification ou une autre commande pour exécuter si elle arrive à expiration. Après un peu de recherche et d'expérimentation, je suis venu avec cette bash script:

if 
    timeout 20s COMMAND_YOU_WANT_TO_EXECUTE;
    timeout 20s AS_MANY_COMMANDS_AS_YOU_WANT;
then
    echo 'OK'; #if you want a positive response
else
    echo 'Not OK';
    AND_ALTERNATIVE_COMMANDS
fi

Script simple avec clarté du code. Enregistrer à /usr/local/bin/run:

#!/bin/bash

# run
# Run command with timeout $1 seconds.

# Timeout seconds
timeout_seconds="$1"
shift

# PID
pid=$$

# Start timeout
(
  sleep "$timeout_seconds"
  echo "Timed out after $timeout_seconds seconds"
  kill -- -$pid &>/dev/null
) &
timeout_pid=$!

# Run
"$@"

# Stop timeout
kill $timeout_pid &>/dev/null

Les temps une commande qui dure trop longtemps:

$ run 2 sleep 10
Timed out after 2 seconds
Terminated
$

se termine immédiatement pour une commande qui complète:

$ run 10 sleep 2
$

Pour le délai d'attente slowcommand après 1 seconde:

timeout 1 slowcommand || echo "I failed, perhaps due to time out"

Si vous connaissez déjà le nom du programme (supposons program) de mettre fin à une fois le délai (comme un exemple de seconde 3), je peux apporter une solution alternative simple et un peu sale:

(sleep 3 && killall program) & ./program

Cela fonctionne parfaitement si j'appelle des processus de référence avec les appels système.

Il y a aussi cratimeout par Martin Cracauer (écrit en C pour les systèmes Unix et Linux).

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 1
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'

OS X ne pas utiliser bash 4 encore, n'a pas non plus / usr / bin / délai d'attente, voici donc une fonction qui fonctionne sur Mac OS X sans-bière maison ou macports qui est similaire à / usr / bin / délai d'attente ( basé sur la réponse de Tino). validation des paramètres, l'aide, l'utilisation et le soutien d'autres signaux sont un exercice pour le lecteur.

# implement /usr/bin/timeout only if it doesn't exist
[ -n "$(type -p timeout 2>&1)" ] || function timeout { (
    set -m +b
    sleep "$1" &
    SPID=${!}
    ("${@:2}"; RETVAL=$?; kill ${SPID}; exit $RETVAL) &
    CPID=${!}
    wait %1
    SLEEPRETVAL=$?
    if [ $SLEEPRETVAL -eq 0 ] && kill ${CPID} >/dev/null 2>&1 ; then
      RETVAL=124
      # When you need to make sure it dies
      #(sleep 1; kill -9 ${CPID} >/dev/null 2>&1)&
      wait %2
    else
      wait %2
      RETVAL=$?
    fi
    return $RETVAL
) }

On m'a présenté un problème pour préserver le contexte de la coquille et laisser les délais d'attente, le seul problème avec elle est-il arrêtera l'exécution du script sur le délai d'attente - mais il est très bien avec les besoins que je présentais:

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

avec les sorties:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

bien sûr, je suppose qu'il y avait un répertoire appelé scripts

#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"

Mon problème était peut-être un peu différent: je commence une commande via ssh sur une machine distante et que vous voulez tuer le shell et Childs si la commande se bloque

.

Je l'utilise maintenant comme suit:

ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'

De cette façon, la commande retourne 255 quand il y avait un délai d'attente ou returnCode de la commande en cas de succès

S'il vous plaît noter que tuer les processus d'une session ssh est gérée différent d'un shell interactif. Mais vous pouvez également utiliser l'option -t ssh pour attribuer un pseudo-terminal, il agit comme un shell interactif

Voici une version qui ne repose pas sur la ponte d'un processus d'enfant - je besoin d'un script autonome qui intégré cette fonctionnalité. Il fait aussi un intervalle de sondage fractionné, de sorte que vous pouvez interroger plus rapide. délai d'attente aurait été préféré - mais je suis coincé sur un ancien serveur

# wait_on_command <timeout> <poll interval> command
wait_on_command()
{
    local timeout=$1; shift
    local interval=$1; shift
    $* &
    local child=$!

    loops=$(bc <<< "($timeout * (1 / $interval)) + 0.5" | sed 's/\..*//g')
    ((t = loops))
    while ((t > 0)); do
        sleep $interval
        kill -0 $child &>/dev/null || return
        ((t -= 1))
    done

    kill $child &>/dev/null || kill -0 $child &>/dev/null || return
    sleep $interval
    kill -9 $child &>/dev/null
    echo Timed out
}

slow_command()
{
    sleep 2
    echo Completed normally
}

# wait 1 sec in 0.1 sec increments
wait_on_command 1 0.1 slow_command

# or call an external command
wait_on_command 1 0.1 sleep 10

Miser sur @ réponse de ... loup

Si vous voulez un délai d'attente processus et réduire au silence la sortie du travail kill / pid, exécutez:

( (sleep 1 && killall program 2>/dev/null) &) && program --version 

Cela met le processus en backgrounded un sous-shell afin que vous ne voyez pas la sortie du travail.

J'ai une tâche cron qui appelle un script php et, quelques fois, il se coince sur le script php. Cette solution était parfaite pour moi.

J'utilise:

scripttimeout -t 60 /script.php

Dans 99% des cas, la réponse est de ne pas mettre en œuvre une logique de délai d'attente. la logique du délai d'attente est dans presque toute situation un signe d'avertissement rouge que quelque chose autre est erroné et doit être fixé au lieu .

Votre pendaison ou rupture processus après n secondes parfois? Ensuite, savoir pourquoi et corriger cela à la place.

En aparté, à faire droit de solution de Strager, vous devez utiliser attente « SPID $ » au lieu de fg 1, car dans les scripts que vous n'avez pas le contrôle de l'emploi (et d'essayer de le mettre en marche est stupide). De plus, fg 1 repose sur le fait que vous ne commencez pas à d'autres emplois précédemment dans le script qui est une mauvaise prise en charge de faire.

Une façon très simpliste:

# command & sleep 5; pkill -9 -x -f "command"

avec pkill (en option -f ), vous pouvez tuer votre commande spécifique avec des arguments ou spécifier -n pour éviter de tuer ancien processus.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top