Frage

Diese Antwort href="https://stackoverflow.com/questions/601543"> Befehlszeile zum

schlägt ein 1-Zeilen-Verfahren zur Herstellung eines lang laufenden Befehl von der Befehlszeile bash timeout:

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

Aber es ist möglich, dass ein gegebener „long-running“ Befehl früher beenden kann als das Timeout. (Nennen wir es eine "typisch-long-running-but-mal-schnell" Befehl oder tlrbsf zum Spaß.)

Also dieser nette 1-liner Ansatz hat ein paar Probleme. Erstens ist die sleep nicht an Bedingungen geknüpft, so dass ein unerwünschten Untersatz gebunden an der Zeit für die Sequenz genommen zu beenden. Betrachten 30s oder 2 m oder sogar 5 m für den Schlaf, wenn die tlrbsf Befehl beendet in 2 Sekunden - höchst unerwünscht. Zweitens ist die kill ist bedingungslos, so dass diese Sequenz wird versuchen, einen nicht-laufenden Prozess zu töten und jammert über sie.

So ...

Gibt es eine Möglichkeit ein typisch-long-running-aber-mal-schnell ( "tlrbsf" ) Befehl,

Timeout
  • eine Bash-Implementierung hat (die andere Frage bereits Perl und C Antworten)
  • wird in der früheren der beiden beenden: tlrbsf Programmabbruch oder Timeout abgelaufen
  • wird nicht töten nicht vorhandene / nicht-laufende Prozesse (oder optional: wird nicht beschweren über ein schlechten kill)
  • nicht über ein 1-Liner
  • sein
  • kann unter Cygwin oder Linux läuft

... und für Bonuspunkte, läuft die tlrbsf Befehl in dem Vordergrund und jedem 'Schlaf' oder zusätzlichen Prozess im Hintergrund, so dass der stdin / stdout / stderr der tlrbsf Befehl umgeleitet werden kann, so, als ob es direkt ausgeführt worden war?

Wenn ja, bitte teilen Sie Ihren Code. Wenn nicht, bitte erklären, warum.

Ich habe verbringe eine Weile versucht, das oben genannte Beispiel zu hacken, aber ich bin die Grenze meiner bash Fähigkeiten zu treffen.

War es hilfreich?

Lösung

Ich denke, das ist genau das, was Sie fordern:

http://www.bashcookbook.com/bashinfo/ Quelle / 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 "$@"

Andere Tipps

Sie suchen wahrscheinlich für den timeout Befehl in coreutils. Da es ein Teil von coreutils ist, ist es technisch eine C-Lösung, aber es ist immer noch coreutils. info timeout für weitere Details. Hier ein Beispiel:

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

Diese Lösung funktioniert unabhängig von bash-Monitor-Modus. Sie können das richtige Signal verwenden zu beenden 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

Der Beobachter tötet your_command nach bestimmten Timeout; das Skript wartet auf die langsame Aufgabe und beendet die Beobachter. Beachten Sie, dass wait nicht mit Prozessen funktioniert, die Kinder einer anderen Schale sind.

Beispiele:

  • your_command läuft länger als 2 Sekunden und wurde beendet
  

your_command unterbrochen

( 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 vor dem Timeout beendet (20 Sekunden)
  

your_command beendet

( 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

Dort gehen Sie:

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

Sie können die SIGINT und 10 ändern, wie Sie wünschen;)

Ich ziehe "Zeitlimit", die ein Paket zumindest in debian hat.

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

Es ist ein bisschen schöner als das coreutils „Timeout“, weil es etwas gedruckt wird, wenn der Prozess zu töten, und es sendet auch SIGKILL nach einiger Zeit in der Standardeinstellung.

Sie können dies tun, ganz mit bash 4.3 und oben:

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

  • Anforderungen Bash 4.3 für wait -n

  • 137 Gibt wenn der Befehl getötet wurde, sonst den Rückgabewert des Befehls.
  • Werke für Rohre. (Sie brauchen nicht Vordergrund, hier zu gehen!)
  • Funktioniert mit internen Shell-Befehlen oder Funktionen auch.
  • Läuft in einer Subshell, so dass kein variable Export in den aktuell Shell, sorry.

Wenn Sie den Return-Code nicht benötigen, kann dies noch einfacher gemacht werden:

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

Weitere Informationen:

  • Genau genommen Sie die ; in ; ) nicht brauchen, aber es ist Sache konsequenter auf den ; }-Fall macht. Und die set +b kann wahrscheinlich weg gelassen werden, auch, aber sicher ist sicher.

  • Mit Ausnahme --forground (wahrscheinlich) können Sie implementieren alle Varianten timeout unterstützt. --preserve-status ist ein bisschen schwierig, aber. Dies wird für den Leser als Übung;)

Dieses Rezept verwendet werden „natürlich“ in der Schale (so natürlich wie für flock fd):

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

jedoch, wie oben erläutert, können Sie nicht Reexport Umgebungsvariablen in den umgebenden natürlich auf diese Weise Shell.

Edit:

Ein echtes Beispiel: Time out __git_ps1, falls es zu lang (für Dinge wie langsam SSHFS-Verbindungen) führt:

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

Edit2: Bugfix. Ich bemerkte, dass exit 137 nicht benötigt wird, und macht _timeout unzuverlässig zugleich.

Edit3: git ist ein hartnäckiger, so braucht es eine Doppel Trick satisfyingly zu arbeiten

.

Edit4: Vergessen eine _ im ersten _timeout für das reale Welt GIT Beispiel

.

Siehe auch die http://www.pixelbeat.org/scripts/timeout Skript die Funktionalität, die in neueren coreutils integriert wurde,

Kinda hacky, aber es funktioniert. Funktioniert nicht, wenn Sie andere Vordergrundprozesse haben (bitte helfen Sie mir, dieses Problem zu beheben!)

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

Eigentlich, denke ich Sie es rückgängig machen können, welche die 'Bonus' Kriterien:

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

timeout ist wahrscheinlich der erste Ansatz zu versuchen. Sie können eine Benachrichtigung oder einen anderen Befehl benötigen, wenn es auszuführen mal aus. Nach einem recht kleinen Suche und Experimentieren, kam ich mit diesem nach oben bash Skript:

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

Einfache Skript mit Code Klarheit. Zu dem /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

Die Zeiten aus einem Befehl, der zu lange läuft:

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

kurz sofort für einen Befehl, der ergänzt:

$ run 10 sleep 2
$

die slowcommand nach 1 Sekunde Zeitüberschreitung:

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

Wenn Sie bereits den Namen des Programms kennen (sich program annimmt) nach dem Timeout zu beenden (als Beispiel 3 Sekunden), habe ich eine einfache und etwas schmutzig alternative Lösung beitragen kann:

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

Das funktioniert perfekt, wenn ich Benchmark-Prozesse mit Systemaufrufen nennen.

Es gibt auch cratimeout von Martin Cracauer (geschrieben in C für Unix und Linux-Systeme).

# 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 nicht bash 4 noch nicht verwendet, noch es hat / usr / bin / timeout, also hier ist eine Funktion, die ohne das Haus zu brauen oder Macports auf OS X arbeitet, die in / usr / bin / timeout ähnlich ist ( auf der Grundlage von Tino Antwort). Parameter Validierung, Hilfe, Nutzung und Unterstützung für andere Signale ist eine Übung für Leser.

# 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
) }

Ich war mit einem Problem stellt die Schale Kontext zu erhalten und erlaube Timeouts, damit das einzige Problem ist es die Skriptausführung auf dem Timeout zu stoppen - aber es ist in Ordnung mit den Bedürfnissen I vorgestellt wurde:

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

mit den Ausgängen:

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

ich natürlich annehmen, dass es war ein Verzeichnis namens 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 "$@"

Mein Problem war vielleicht ein bisschen anders: Ich habe einen Befehl via ssh auf einem entfernten Rechner starten und will die Schale und Childs, wenn der Befehl hängt töten

.

ich jetzt verwenden wie folgt vor:

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

Auf diese Weise der Befehl 255 zurückgibt, wenn ein Timeout oder die Rückkehr des Befehls im Falle des Erfolgs war

Bitte beachten Sie, dass Prozesse aus einer SSH-Sitzung zu töten behandelt wird unterscheidet sich von einer interaktiven Shell. Sie können aber auch die Option -t verwenden, um ssh einen Pseudo-Terminal zu verteilen, so dass es wirkt wie eine interaktive Shell

Dies ist eine Version, die nicht auf angewiesen ist, ein Kind Prozess Laichen - ich brauchte ein eigenständiges Skript, das diese Funktionalität eingebettet. Es spielt auch eine gebrochene Pollintervall, so dass Sie schneller abfragen können. Timeout hätte es vorgezogen worden - aber ich bin auf einem alten Server steckt

# 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

Aufbauend auf @ loup Antwort ...

Wenn Sie einen Prozess Timeout wollen und zum Schweigen bringen die Abtötung Job / pid Ausgang, führen Sie:

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

Damit ist der backgrounded Prozess in ein Sub-Shell, so dass Sie den Job Ausgang nicht sehen.

Ich habe einen cron-Job, der ein PHP-Skript und einige Male ruft es auf PHP-Skript stecken. Diese Lösung war perfekt für mich.

ich benutze:

scripttimeout -t 60 /script.php

In 99% der Fälle ist die Antwort nicht jede Timeout Logik zu implementieren. Timeout-Logik ist in nahezu jeder Situation ein rotes Warnsignal, dass etwas sonst ist falsch und sollte festgelegt werden statt .

Ist Ihr Prozess hängen oder Brechen nach n Sekunden manchmal? Dann finden Sie heraus, warum und beheben, statt.

Als Nebenwirkung tun strager Lösung richtig, Sie müssen warten, verwenden „$ SPID“ anstelle von fg 1, da in Skripten Sie keine Job-Kontrolle (und versuchen, es zu aktivieren, ist dumm). Außerdem fg 1 beruht auf der Tatsache, dass Sie keine anderen Jobs vorher im Skript gestartet wurden, die eine schlechte Annahme ist, zu machen.

Eine sehr simple Art und Weise:

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

mit pkill (Option -f ) Sie Ihren spezifischen Befehl mit Argumenten töten oder -n angeben, um alten Prozess zu vermeiden töten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top