مهلة أمر في bash دون تأخير غير ضروري




command-line timeout (13)

أعتقد أن هذا بالضبط ما تطلبه:

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 <[email protected]>

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 "[email protected]"

هذه الإجابة على أمر سطر الأوامر لقتل أمر بعد فترة معينة من الوقت

يقترح طريقة سطر واحد لمهلة أمر تشغيل طويل من سطر أوامر bash:

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

ولكن من المحتمل أن ينتهي أمر معين "طويل الأمد" قبل انتهاء المهلة المحددة. (دعنا نسميها أمر "عادةً تشغيل - طويل - ولكن - في بعض الأحيان - سريع" ، أو tlrbsf للمتعة.)

لذلك فإن هذا النهج البسيط 1-liner لديه بعض المشاكل. أولاً ، لا يكون sleep مشروطًا ، بحيث يتم تعيين حد أدنى غير مرغوب فيه على الوقت المستغرق للتسلسل حتى النهاية. خذ بعين الاعتبار 30s أو 2m أو حتى 5m للنوم ، عند انتهاء الأمر tlrbsf في ثانيتين - غير مرغوب فيه إلى حد كبير. ثانياً ، kill غير مشروط ، لذا فإن هذا التسلسل سيحاول قتل عملية غير جارية ويخشى عليها.

وبالتالي...

هل هناك طريقة لإنهاء أمر عادة طويلة - تشغيل - ولكن - في بعض الأحيان سريع ( "tlrbsf" )

  • لديه تطبيق bash (يحتوي السؤال الآخر على إجابات Perl و C)
  • سوف تنتهي في وقت سابق من الاثنين: إنهاء برنامج tlrbsf ، أو انقضت مهلة
  • لن تقتل العمليات غير القائمة / غير الجارية (أو ، اختياريًا: لن تشتكي من قتل سيء)
  • ليس من الضروري أن يكون 1-بطانة
  • يمكن أن تعمل تحت Cygwin أو Linux

... وبالنسبة لنقاط المكافأة ، يتم تشغيل الأمر tlrbsf في المقدمة وأي "نوم" أو عملية إضافية في الخلفية ، بحيث يمكن إعادة توجيه stdin / stdout / stderr للأمر tlrbsf ، كما لو كان قد تم تشغيل مباشرة؟

إذا كان الأمر كذلك ، يرجى مشاركة رمزك. إذا لم يكن كذلك، يرجى توضيح السبب.

لقد قضيت وقتا طويلا في محاولة لاختراق المثال المذكور ولكنني أضطر إلى الحد من مهاراتي باش.


أنا أفضل "timelimit" ، الذي يحتوي على حزمة على الأقل في ديبيان.

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

إنها أجمل قليلاً من "المهلة" الأساسية لأنها تطبع شيئا عند قتل العملية ، كما أنها ترسل SIGKILL بعد مرور بعض الوقت بشكل افتراضي.


برنامج نصي بسيط مع وضوح الرمز. حفظ إلى /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
"[email protected]"

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

اختصار لأمر يعمل لفترة طويلة جدًا:

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

ينتهي على الفور لأمر يكمل:

$ run 10 sleep 2
$


ربما كنت تبحث عن أمر timeout في coreutils. وبما أنه جزء من النواة ، فإنه من الناحية الفنية حل C ، لكنه لا يزال coreutils. info timeout لمزيد من التفاصيل. إليك مثال على ذلك:

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

ربما يكون المهلة هي المقاربة الأولى للمحاولة. قد تحتاج إلى إعلام أو أمر آخر للتنفيذ في حالة انتهاء المهلة. بعد قليل من البحث والتجريب ، توصلت إلى هذا البرنامج النصي bash :

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

عرضت عليّ مشكلة للحفاظ على سياق الصدفة والسماح بالمهلة ، والمشكلة الوحيدة معها هي أنها ستوقف تنفيذ النص البرمجي في المهلة - ولكن لا بأس بالاحتياجات التي تم تقديمها:

#!/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=$!
  "[email protected]" || _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

مع المخرجات:

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

بالطبع أفترض أن هناك ما يسمى بالبرامج scripts


في 99٪ من الحالات ، لا يكون الجواب هو تنفيذ أي منطق للمهلة. يُعد منطق المهلة في أي موقف تقريبًا إشارة تحذير حمراء تفيد بأن هناك خطأ آخر ويجب إصلاحه بدلاً من ذلك .

هل يتم تعليق العملية أو انقطاعها بعد n من الثواني أحيانًا؟ ثم اكتشف لماذا وإصلاح ذلك بدلا من ذلك.

كإجراء جانبي ، للقيام بحل strager ، تحتاج إلى استخدام الانتظار "$ SPID" بدلاً من fg 1 ، حيث أنه في البرامج النصية ليس لديك وظيفة التحكم (ومحاولة تشغيله هو غبي). علاوة على ذلك ، يعتمد fg 1 على حقيقة أنك لم تبدأ أي وظائف أخرى سابقة في البرنامج النصي الذي يعتبر افتراضًا سيئًا.


كندة هاكي ، لكنها تعمل. لا يعمل إذا كان لديك عمليات مقدمة أخرى (الرجاء مساعدتي في إصلاح هذا!)

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

في الواقع ، أعتقد أنه يمكنك عكسها ، وتلبية معايير "المكافأة" الخاصة بك:

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

لا يستخدم OS X bash 4 حتى الآن ، ولا يحتوي على / usr / bin / timeout ، لذا إليك وظيفة تعمل على OS X بدون المشروب المنزلي أو macports مشابهًا لـ / usr / bin / timeout (استنادًا إلى Tino's إجابة). التحقق من صحة المعلمة والمساعدة والاستخدام والدعم للإشارات الأخرى هي تمرين للقارئ.

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

هناك أيضا cratimeout بواسطة مارتن Cracauer (مكتوبة في C لأنظمة يونيكس ولينكس).

# 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'

يعمل هذا الحل بغض النظر عن وضع bash monitor. يمكنك استخدام الإشارة المناسبة لإنهاء 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

يقتل المراقبك your_command بعد انتهاء المهلة المحددة ؛ البرنامج النصي ينتظر المهمة البطيئة وينهي المراقبين. لاحظ أن wait لا يعمل مع العمليات التي هي الأطفال من قذيفة مختلفة.

أمثلة:

  • يتم تشغيل your_command أكثر من ثانيتين وتم إنهاؤه

your_command

( 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 قبل انتهاء المهلة (20 ثانية)

your_command انتهى

( 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

#! /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 "[email protected]"




utilities