หมดเวลาคำสั่งในการทุบตีโดยไม่ต้องล่าช้าไม่จำเป็น


283

คำตอบสำหรับคำสั่งบรรทัดคำสั่งนี้เพื่อฆ่าคำสั่งโดยอัตโนมัติหลังจากระยะเวลาหนึ่ง

เสนอวิธี 1 บรรทัดเพื่อหมดเวลาคำสั่งที่รันเป็นเวลานานจากบรรทัดคำสั่ง bash:

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

แต่เป็นไปได้ว่าคำสั่ง "ที่รันนาน" อาจเสร็จสิ้นเร็วกว่าการหมดเวลา (ลองเรียกมันว่าคำสั่ง "โดยปกติจะใช้เวลานาน แต่บางครั้งเร็ว" หรือคำสั่งtlrbsfเพื่อความสนุกสนาน)

ดังนั้นวิธีการ 1 ซับที่ดีนี้มีปัญหาสองสามอย่าง อย่างแรกsleepคือไม่มีเงื่อนไขดังนั้นจึงกำหนดขอบเขตล่างที่ไม่พึงประสงค์ในเวลาที่ใช้เพื่อให้ลำดับเสร็จ พิจารณา 30s หรือ 2m หรือ 5m สำหรับการสลีปเมื่อคำสั่งtlrbsfเสร็จสิ้นใน 2 วินาที - ไม่พึงปรารถนาอย่างมาก ประการที่สองkillคือไม่มีเงื่อนไขดังนั้นลำดับนี้จะพยายามฆ่ากระบวนการที่ไม่ทำงานและสะอื้นเกี่ยวกับมัน

ดังนั้น...

มีวิธีการหมดเวลาคำสั่งโดยทั่วไปยาว - วิ่ง - แต่ - บางครั้ง - เร็ว ( "tlrbsf" ) คำสั่งที่

  • มีการดำเนินการทุบตี (คำถามอื่น ๆ แล้วมีคำตอบ Perl และ C)
  • จะยุติการทำงานเมื่อสิ้นสุดโปรแกรมทั้งสอง: tlrbsfหรือหมดเวลาที่ผ่านไป
  • จะไม่ฆ่ากระบวนการที่ไม่มีอยู่ / ไม่ทำงาน (หรือเป็นทางเลือก: จะไม่บ่นเกี่ยวกับการฆ่าที่ไม่ดี)
  • ไม่จำเป็นต้องเป็น 1 ซับ
  • สามารถทำงานภายใต้ Cygwin หรือ Linux

... และสำหรับคะแนนโบนัสให้รันคำสั่งtlrbsfในเบื้องหน้าและ 'โหมดสลีป' หรือกระบวนการพิเศษในพื้นหลังเช่นว่า stdin / stdout / stderr ของคำสั่งtlrbsfสามารถเปลี่ยนเส้นทางได้เหมือนกับว่ามันเป็น ทำงานโดยตรงหรือไม่

ถ้าเป็นเช่นนั้นโปรดแบ่งปันรหัสของคุณ ถ้าไม่ได้โปรดอธิบายว่าทำไม

ฉันใช้เวลาสักครู่ในการแฮ็คตัวอย่างที่กล่าวมาแล้ว แต่ฉันใช้ทักษะการทุบตีของฉันไม่ทัน


5
อีกคำถามที่คล้ายกัน: stackoverflow.com/questions/526782/… (แต่ฉันคิดว่าคำตอบ 'หมดเวลา 3' ที่นี่ดีกว่ามาก)
ระบบหยุดชั่วคราว

2
เหตุผลใดที่ไม่ใช้timeoutยูทิลิตีgnu ?
Chris Johnson

timeoutดีมาก! คุณสามารถใช้กับหลายคำสั่ง (สคริปต์หลายบรรทัด): stackoverflow.com/a/61888916/658497
Noam Manos

คำตอบ:


149

ฉันคิดว่านี่เป็นสิ่งที่คุณต้องการอย่างแน่นอน:

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 "$@"

นั่นเป็นกลอุบายที่แหลมคมโดยใช้ $$ สำหรับส่วนพื้นหลัง และดีที่มันจะฆ่า tlrbsf แขวนทันที แต่คุณต้องเลือกช่วงเวลาเลือกตั้ง และถ้าคุณตั้งค่าการโพลต่ำเกินไปมันจะกิน CPU ที่มีการส่งสัญญาณคงที่ทำให้ tlrbsf ทำงานได้นานขึ้น!
ระบบหยุดชั่วคราว

7
คุณไม่ต้องเลือกช่วงเวลาการโพล แต่ก็มีค่าเริ่มต้นที่ 1 ซึ่งค่อนข้างดี และการตรวจสอบนั้นไม่แพงมากค่าใช้จ่ายเล็กน้อย ฉันสงสัยว่าจะทำให้ tlrbsf ทำงานได้นานขึ้นอย่างเห็นได้ชัด ฉันทดสอบกับ sleep 30 และมีความแตกต่างระหว่าง 0.000ms ระหว่างการใช้และไม่ใช้
Juliano

6
ใช่ฉันเห็นแล้ว และเป็นไปตามข้อกำหนดที่แน่นอนของฉันหากคุณตั้งค่าช่วงโพล == ไทม์เอาต์ ยังทำงานในท่อทำงานกับพื้นหลังทั้งหมดทำงานกับหลายอินสแตนซ์และงานอื่น ๆ ที่ทำงานอยู่ น่ารักขอบคุณ!
ระบบหยุดชั่วคราว

การส่งสัญญาณฆ่า sub-shell ดังนั้นฉันคิดว่าการซับคำสั่ง kill ทั้งหมดในหนึ่งบรรทัดจะรักษาไว้ ฉันยังเปิดใช้งานเอาต์พุต stderr เพื่อแสดงข้อผิดพลาดที่ไม่คาดคิด stackoverflow.com/questions/687948/…
eel ghEEz

1
@Juliano นั่นเป็นวิธีที่ยอดเยี่ยมในการจัดการกับการหมดเวลาซึ่งมีประโยชน์มาก ฉันสงสัยว่ามีวิธีที่เราสามารถให้สคริปต์ returncode 143 เมื่อกระบวนการถูกฆ่าหลังจากหมดเวลาหรือไม่ ฉันพยายามเพิ่ม "exit 143" หลังจากคำสั่ง kill แต่ฉันจะได้รับรหัสทางออก 0 เสมอที่สคริปต์ของผู้โทร
Salman A. Kagzi

528

คุณอาจกำลังมองหาtimeoutคำสั่งใน coreutils เนื่องจากเป็นส่วนหนึ่งของ coreutils มันเป็นเทคนิคการแก้ปัญหา C แต่ยังคง coreutils info timeoutสำหรับรายละเอียดเพิ่มเติม นี่คือตัวอย่าง:

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

21
ใน Mac คุณสามารถติดตั้งผ่าน Macports หรือ homebrew
Ivan Z. Siu

23
เมื่อติดตั้งผ่าน homebrew บน OS X คำสั่งจะกลายเป็นgtimeout
ethicalhack3r

5
... คุณใช้ระบบปฏิบัติการใดที่มี coreutils ตั้งแต่ก่อนปี 2003?
Keith


9
ชี้แจงคำสั่ง homebrew ที่คุณต้องการใน OSX / Mac เป็นและจากนั้นคุณสามารถใช้brew install coreutils gtimeout
Ohad Schneider

37

โซลูชันนี้ใช้งานได้โดยไม่คำนึงถึงโหมดจอภาพ bash คุณสามารถใช้สัญญาณที่เหมาะสมเพื่อยุติ 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 รันมากกว่า 2 วินาทีและถูกยกเลิก

your_ คำสั่งถูกขัดจังหวะ

( 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_ คำสั่งเสร็จสิ้นก่อนหมดเวลา (20 วินาที)

your_ คำสั่งเสร็จสิ้น

( 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

1
waitส่งคืนสถานะการออกของกระบวนการที่รอ ดังนั้นหากคำสั่งของคุณจะออกจากภายในเวลาที่กำหนด your_command interruptedแต่ไม่ใช่ศูนย์สถานะออกแล้วตรรกะที่นี่จะทำงานตามที่มันหมดเวลาเช่นการพิมพ์ แต่คุณสามารถทำได้waitโดยไม่ต้องมีifแล้วตรวจสอบว่า$watcherpid ยังคงมีอยู่ถ้ามันแล้วคุณรู้ว่าคุณไม่ได้หมดเวลา
George Hawkins

ฉันไม่สามารถเข้าใจได้ว่าทำไมสิ่งนี้ถึงใช้killในกรณีหนึ่ง แต่pkillในอีกกรณีหนึ่ง ฉันต้องใช้pkillทั้งสองอย่างเพื่อให้งานนี้ถูกต้อง ฉันคาดหวังว่าถ้าคุณห่อคำสั่ง()แล้วคุณจะต้องใช้pkillเพื่อฆ่ามัน ()แต่บางทีมันอาจจะทำงานแตกต่างกันหากมีเพียงคำสั่งเดียวภายใน
สูงศักดิ์

ขอพระเจ้าคุ้มครองท่านด้วย :)
Darko Miletic

22

ไปแล้ว:

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

คุณสามารถเปลี่ยนSIGINTและ10ตามที่คุณต้องการ;)


3
"หมดเวลา" เป็นส่วนหนึ่งของแพ็คเกจcoreutilsบน (อย่างน้อย) Redhat, Centos, Suse และ Ubuntu ดังนั้นคุณจะต้องติดตั้งถ้าคุณไม่มี
Akom

มันมีประโยชน์จริงๆ !!!!!! คุณรู้หรือไม่ว่าเหตุใด "การหมดเวลา 5 / เส้นทาง / ถึง / ช้า / คำสั่งพร้อมตัวเลือก" ของ yingted บางครั้งไม่ทำงาน
Decula

น่าเสียดายที่นี่ไม่ได้อยู่ในcoreutilsแพ็คเกจของ FreeBSD
Paul Bissex

18

คุณสามารถทำได้ทั้งด้วยbash 4.3และเหนือ:

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

  • ต้องการ Bash 4.3 สำหรับ wait -n

  • ให้ 137 หากคำสั่งถูกฆ่าตายมิฉะนั้นค่าส่งคืนของคำสั่ง
  • ใช้งานได้กับท่อ (คุณไม่จำเป็นต้องไปเบื้องหน้าที่นี่!)
  • ทำงานร่วมกับคำสั่งหรือฟังก์ชั่นเชลล์ภายในได้เช่นกัน
  • ทำงานในเชลล์ย่อยดังนั้นจึงไม่มีการส่งออกตัวแปรไปยังเชลล์ปัจจุบันขออภัย

หากคุณไม่ต้องการรหัสส่งคืนสามารถทำได้ง่ายกว่า:

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

หมายเหตุ:

  • พูดอย่างเคร่งครัดคุณไม่จำเป็นต้อง;อยู่ใน; )แต่มันทำให้สิ่งที่สอดคล้องกันมากขึ้นไป; }-case และset +bอาจจะถูกทิ้งไว้เช่นกัน แต่ปลอดภัยกว่าดีกว่าขออภัย

  • ยกเว้น--forground(อาจ) คุณสามารถใช้การtimeoutสนับสนุนรูปแบบทั้งหมดได้ --preserve-statusแม้ว่าจะค่อนข้างยากก็ตาม นี่เป็นแบบฝึกหัดสำหรับผู้อ่าน;)

สูตรนี้สามารถใช้ "เป็นธรรมชาติ" ในเปลือก (เป็นธรรมชาติสำหรับflock fd):

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

อย่างไรก็ตามตามที่อธิบายไว้ข้างต้นคุณไม่สามารถส่งออกตัวแปรสภาพแวดล้อมอีกครั้งลงในเชลล์ที่ล้อมรอบด้วยวิธีนี้ได้ตามธรรมชาติ

แก้ไข:

ตัวอย่างในโลกแห่งความเป็นจริง: การหมดเวลา__git_ps1ในกรณีที่ใช้เวลานานเกินไป (สำหรับสิ่งต่างๆเช่นลิงก์ SSHFS ช้า):

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

แก้ไข 2: แก้ไขข้อผิดพลาด ฉันสังเกตเห็นว่าexit 137ไม่จำเป็นและทำให้_timeoutไม่น่าเชื่อถือในเวลาเดียวกัน

แก้ไข 3: gitเป็นการตายยากดังนั้นจึงต้องใช้เคล็ดลับสองครั้งในการทำงานที่น่าพอใจ

แก้ไข 4: ลืมสิ่ง_แรก_timeoutในตัวอย่าง GIT โลกแห่งความจริง


1
ทุบหิน 4 ก้อน นั้นคือทั้งหมด.
ระบบหยุดชั่วคราว

1
สิ่งนี้ต้องการ Bash 4.3 หรือใหม่กว่า cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status. จาก: tiswww.case.edu/php/chet/bash/NEWS
Ben Reser

17

ฉันชอบ "timelimit" ซึ่งมีแพ็คเกจอย่างน้อยเป็นเดเบียน

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

มันค่อนข้างดีกว่า coreutils "timeout" เพราะมันพิมพ์บางอย่างเมื่อฆ่ากระบวนการและมันก็ส่ง SIGKILL หลังจากเวลาผ่านไปตามค่าเริ่มต้น


ดูเหมือนว่าจะทำงานได้ไม่ดีนัก: / $ time timelimit -T2 sleep 10 ผู้ใช้ 0m10.003s จริง 0m0.000s sys 0m0.000s
hithwen

3
ใช้ -t2 ไม่ใช่ -T2 big -T คือเวลาจากการส่ง SIGTERM จนกระทั่งส่ง SIGKILL
maxy

1
ฉันต้องการเพิ่ม timelimit 1.8 นั้นไม่สามารถทำงานได้ดีด้วย fork ( timelimit -t1 ./a_forking_progฆ่าเพียงหนึ่งในสองกระบวนการ) แต่การหมดเวลาใช้งาน
Jeremy Cochoy

หากคุณต้องการหมดเวลาที่จะพิมพ์บางอย่างเมื่อฆ่ากระบวนการเพียงใช้แฟล็ก "-v"

10

หากต้องการหมดเวลาslowcommandหลังจาก 1 วินาที:

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

ในการพิจารณาว่าคำสั่งหมดเวลาหรือล้มเหลวด้วยเหตุผลของตนเองหรือไม่ให้ตรวจสอบว่ารหัสสถานะคือ 124:

# ping for 3 seconds, but timeout after only 1 second
timeout 1 ping 8.8.8.8 -w3
EXIT_STATUS=$?
if [ $EXIT_STATUS -eq 124 ]
then
echo 'Process Timed Out!'
else
echo 'Process did not timeout. Something else went wrong.'
fi
exit $EXIT_STATUS

โปรดทราบว่าเมื่อสถานะทางออกคือ 124 คุณไม่ทราบว่าหมดเวลาเนื่องจากtimeoutคำสั่งของคุณหรือไม่หรือคำสั่งนั้นสิ้นสุดลงเนื่องจากตรรกะการหมดเวลาภายในของตัวเองแล้วส่งคืน 124 คุณสามารถสันนิษฐานได้อย่างปลอดภัยทั้งสองกรณี แม้ว่าจะมีการหยุดพักชั่วคราวเกิดขึ้น


9

ดูเพิ่มเติมที่http://www.pixelbeat.org/scripts/timeoutสคริปต์การใช้งานซึ่งรวมเข้ากับ coreutils ที่ใหม่กว่า


เรียบร้อยง่ายใช้ TERM ไม่ใช่ KILL ดี! ฉันสำรวจกับดัก / วิธีแก้ปัญหาแบบนี้เมื่อตอนที่ฉันโพสต์คำถาม
ระบบหยุดชั่วคราว

การหมดเวลาส่งคืน 124 ในกรณีของการฆ่า
ceving

8

ค่อนข้างแฮ็ก แต่มันใช้งานได้ดี ไม่ทำงานหากคุณมีกระบวนการพื้นหน้าอื่น ๆ (โปรดช่วยฉันแก้ไขสิ่งนี้!)

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

(ls -ltR / cygdrive / c / windows & SPID = $ {!}; (พัก 1 วินาที; ฆ่า $ {SPID}) & CPID = $ {!}; fg 1; ฆ่า $ {CPID})> fdsa
ระบบหยุด

bash: fg: ไม่มีการควบคุมงาน
ระบบ PAUSE

@ ระบบหยุดชั่วคราวตั้งค่า -m ฉันคิดว่า
แปลกหน้า

ฉันมี job control (set -m) ใน login shell นั่นคือ 'm' ในเนื้อหาของ himBH ของ $ - แต่ดูเหมือนว่าจะหายไปสำหรับ subshells อาจเป็นสิ่งประดิษฐ์ Cygwin grumble
ระบบหยุดชั่วคราว

อย่าใช้ "fg" ในสคริปต์ อ่าน "ช่วยรอ"
lhunath

8

การหมดเวลาอาจเป็นวิธีแรกในการลอง คุณอาจต้องการการแจ้งเตือนหรือคำสั่งอื่นเพื่อดำเนินการถ้าหมดเวลา หลังจากค้นหาและทดลองมาซักพักฉันก็พบกับสคริปต์ทุบตีนี้:

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

5

สคริปต์อย่างง่ายพร้อมความคมชัดของรหัส บันทึกไปที่/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

หมดเวลาคำสั่งที่รันนานเกินไป:

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

สิ้นสุดทันทีสำหรับคำสั่งที่เสร็จสมบูรณ์:

$ run 10 sleep 2
$

3

หากคุณรู้ชื่อของโปรแกรม (สมมติว่าprogram) ยุติหลังจากหมดเวลา (ตัวอย่าง3วินาที) ฉันสามารถมีส่วนร่วมในการแก้ปัญหาทางเลือกที่เรียบง่ายและสกปรก:

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

สิ่งนี้ทำงานได้อย่างสมบูรณ์ถ้าฉันเรียกกระบวนการเบนช์มาร์กด้วยการเรียกระบบ


2
วิธีนี้จะฆ่ากระบวนการอื่น ๆ ที่เกิดขึ้นกับการใช้ชื่อและไม่ฆ่ากระบวนการด้วยชื่อที่กำหนดถ้ามันเปลี่ยนชื่อ (เช่นโดยการเขียนเข้าไปargv[0]อาจจะมีแฮ็คอื่น ๆ เพื่อเพิ่มพื้นที่)
Jed

ฉันพบความหลากหลายของสิ่งนี้มีประโยชน์เมื่อพยายามหยุดตู้คอนเทนเนอร์นักเทียบท่าหลังจากเวลาผ่านไประยะหนึ่ง ดูเหมือนว่าไคลเอ็นต์ท่าเทียบเรือจะไม่ยอมรับ TERM / INT / KILL ในลักษณะที่จะหยุดคอนเทนเนอร์ที่ทำงานในเบื้องหน้าจริงๆ ดังนั้นการตั้งชื่อคอนเทนเนอร์และการใช้(sleep 3 && docker stop <container>) &งานให้เป็นอย่างดี ขอบคุณ!
Mat Schaffer

ใช่มันสกปรกและมีข้อ จำกัด แต่สามารถปรับปรุงได้เช่น: { sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"ด้วยวิธีนี้มันจะฆ่าเฉพาะงานในเชลล์ปัจจุบัน
ตัน

2

นอกจากนี้ยังมีcratimeoutMartin Cracauer (เขียนเป็นภาษา C สำหรับระบบ Unix และ 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'

"โดยเฉพาะมันจะรักษาพฤติกรรมของสัญญาณ" ยินดีที่ได้มีตัวเลือก!
ระบบหยุดชั่วคราว

1

OS X ยังไม่ได้ใช้ bash 4 และยังไม่มี / usr / bin / timeout ดังนั้นนี่คือฟังก์ชันที่ทำงานบน OS X โดยไม่มี home-brew หรือ 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
) }

0

ใน 99% ของกรณีคำตอบคือไม่ต้องใช้ตรรกะหมดเวลา ตรรกะหมดเวลาอยู่ในเกือบทุกสถานการณ์เป็นสัญญาณเตือนภัยสีแดงที่บางสิ่งบางอย่างอื่นที่ไม่ถูกต้องและควรได้รับการแก้ไขแทนแทน

กระบวนการของคุณค้างหรือหยุดหลังจาก n วินาทีในบางครั้งหรือไม่ จากนั้นหาสาเหตุและแก้ไขแทน

นอกจากวิธีแก้ปัญหาของคนแปลกหน้าคุณต้องใช้การรอ "$ SPID" แทน fg 1 เนื่องจากในสคริปต์คุณไม่มีการควบคุมงาน ยิ่งกว่านั้น fg 1 ขึ้นอยู่กับความจริงที่ว่าคุณไม่ได้เริ่มงานอื่น ๆ ก่อนหน้านี้ในสคริปต์ซึ่งเป็นข้อสันนิษฐานที่ไม่ดี


4
ด้วยการเข้าถึงแหล่งที่มา 100% (และฮาร์ดแวร์ส่วนใหญ่เช่นสวิตช์เครือข่าย) ฉันยอมรับว่าอาจมีวิธีแก้ไขที่ดีกว่าการหมดเวลา แต่เมื่อ 'tlrbsf' เป็นแหล่งปิดไบนารีเท่านั้นบางครั้งคุณต้องแก้ไขข้อ จำกัด นั้น
ระบบหยุดชั่วคราว

@lhunath "ในสคริปต์ที่คุณไม่มีการควบคุมงาน (และพยายามที่จะเปิดใช้งานเป็นโง่)" - โปรดอธิบายที่นี่: stackoverflow.com/questions/690266/…
ระบบหยุดชั่วคราว

@system PAUSE: ตอบstackoverflow.com/questions/690266/…ถูกต้องฉันยังแสดงความคิดเห็นไว้
lhunath

29
lununath สิ่งที่คุณพูดไม่สมเหตุสมผล มีหลายกรณีที่การหมดเวลาเป็นตัวเลือกที่ดีเช่นเมื่อใดก็ตามที่คุณต้องข้ามเครือข่าย
Nate Murray

ข้อยกเว้น: คุณกำลังเขียนสคริปต์ทดสอบเพื่อตรวจสอบว่าซอฟต์แวร์ที่รันอยู่แล้วไม่มีปัญหาการหมดเวลาหรือไม่
peterh - Reinstate Monica

0

ฉันถูกนำเสนอด้วยปัญหาในการรักษาบริบทเชลล์และอนุญาตให้หมดเวลาปัญหาเดียวที่มีคือมันจะหยุดการทำงานของสคริปต์ในการหมดเวลา - แต่ก็ไม่เป็นไรกับความต้องการที่ฉันนำเสนอ:

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

ด้วยเอาท์พุท:

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

แน่นอนฉันคิดว่ามี dir ที่เรียกว่า scripts


0
#! /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 "$@"

@Tino ขอโทษฉันลืมว่าทำไมฉันถึงเปลี่ยนบรรทัดการยุติกระบวนการและทำไมฉันจึงคิดว่านี่เป็นเรื่องสำคัญที่จะต้องแบ่งปัน เสียดายที่ฉันไม่ได้เขียนมันลงไป บางทีฉันพบว่าฉันต้องหยุดก่อนที่จะตรวจสอบความสำเร็จของ kill -s TERM สคริปต์ปี 2008 จากหนังสือทำอาหารดูเหมือนว่าจะตรวจสอบสถานะของกระบวนการทันทีหลังจากส่ง SIGTERM อาจนำไปสู่ข้อผิดพลาดที่พยายามส่ง SIGKILL ไปยังกระบวนการที่เสียชีวิต
ปลาไหล gEEEE

0

ปัญหาของฉันอาจแตกต่างออกไปเล็กน้อย: ฉันเริ่มต้นคำสั่งผ่าน ssh บนเครื่องระยะไกลและต้องการฆ่าเชลล์และลูกถ้าคำสั่งหยุดทำงาน

ตอนนี้ฉันใช้สิ่งต่อไปนี้:

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

วิธีนี้คำสั่งส่งคืน 255 เมื่อหมดเวลาใช้งานหรือ returncode ของคำสั่งในกรณีที่ประสบความสำเร็จ

โปรดทราบว่ากระบวนการฆ่าจากเซสชัน ssh นั้นแตกต่างจากเปลือกโต้ตอบ แต่คุณยังสามารถใช้อ็อพชัน -t เพื่อ ssh เพื่อจัดสรรเทอร์มินัลเทียมดังนั้นจึงทำหน้าที่เหมือนเชลล์แบบโต้ตอบ


0

นี่คือรุ่นที่ไม่พึ่งพาการวางไข่กระบวนการเด็ก - ฉันต้องการสคริปต์แบบสแตนด์อโลนซึ่งฝังฟังก์ชันการทำงานนี้ นอกจากนี้ยังมีช่วงโพลที่เป็นเศษส่วนเพื่อให้คุณสามารถสำรวจได้เร็วขึ้น การหมดเวลาเป็นที่ต้องการ - แต่ฉันติดอยู่บนเซิร์ฟเวอร์เก่า

# 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

-1

วิธีที่ง่ายมาก:

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

ด้วยpkill (ตัวเลือก-f ) คุณสามารถฆ่าคำสั่งเฉพาะของคุณด้วยข้อโต้แย้งหรือระบุ -n เพื่อหลีกเลี่ยงการฆ่ากระบวนการเก่า


คุณรู้ว่านี่เป็นสิ่งที่ OP มีอยู่ในโพสต์ของเขาและสิ่งที่เขาระบุว่าเขาไม่ต้องการใช่ไหม เพราะมันจะรอการหน่วงเวลาการนอนหลับเต็มเสมอ
Etan Reisner

-1

สร้างคำตอบของ @ loup ...

หากคุณต้องการหมดเวลาของกระบวนการและปิดใช้งาน kill job / pid เอาต์พุตให้รัน:

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

สิ่งนี้ทำให้กระบวนการที่อยู่เบื้องหลังเป็น subshell ดังนั้นคุณจะไม่เห็นผลลัพธ์ของงาน


-3

ฉันมีงาน cron ที่เรียกสคริปต์ php และบางครั้งมันติดอยู่กับสคริปต์ php ทางออกนี้สมบูรณ์แบบสำหรับฉัน

ฉันใช้:

scripttimeout -t 60 /script.php

1
คือscripttimeoutอะไร
rudolfbyker
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.