Hết thời gian một lệnh trong bash mà không có độ trễ không cần thiết


283

Câu trả lời này cho lệnh Dòng lệnh để tự động hủy lệnh sau một khoảng thời gian nhất định

đề xuất phương thức 1 dòng để hết thời gian chạy lệnh từ dòng lệnh bash:

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

Nhưng có thể một lệnh "chạy dài" nhất định có thể kết thúc sớm hơn thời gian chờ. (Chúng ta hãy gọi nó là lệnh "thường chạy dài nhưng đôi khi nhanh" hoặc tlrbsf cho vui.)

Vì vậy, cách tiếp cận 1-liner tiện lợi này có một vài vấn đề. Đầu tiên, sleepkhông có điều kiện, do đó đặt giới hạn thấp hơn không mong muốn về thời gian thực hiện để chuỗi kết thúc. Cân nhắc 30 giây hoặc 2m hoặc thậm chí 5m cho giấc ngủ, khi lệnh tlrbsf kết thúc sau 2 giây - rất không mong muốn. Thứ hai, killlà vô điều kiện, vì vậy trình tự này sẽ cố gắng giết một quá trình không chạy và than vãn về nó.

Vì thế...

Có cách nào để hết thời gian một lệnh thường chạy dài nhưng đôi khi nhanh ( "tlrbsf" ) mà

  • có triển khai bash (câu hỏi khác đã có câu trả lời Perl và C)
  • sẽ chấm dứt vào đầu của hai: chấm dứt chương trình tlrbsf hoặc hết thời gian
  • sẽ không giết các quy trình không tồn tại / không chạy (hoặc, tùy chọn: sẽ không phàn nàn về việc giết người xấu)
  • không phải là một lớp lót
  • có thể chạy dưới Cygwin hoặc Linux

... và, đối với các điểm thưởng, chạy lệnh tlrbsf ở nền trước và bất kỳ 'giấc ngủ' hoặc quá trình bổ sung nào trong nền, sao cho lệnh stdin / stdout / stderr của lệnh tlrbsf có thể được chuyển hướng, giống như khi nó được chuyển hướng chạy trực tiếp?

Nếu vậy, xin vui lòng chia sẻ mã của bạn. Nếu không, hãy giải thích lý do tại sao.

Tôi đã dành một lúc để cố gắng hack ví dụ đã nói ở trên nhưng tôi đang đạt đến giới hạn về kỹ năng bash của mình.


5
Một câu hỏi tương tự khác: stackoverflow.com/questions/526782/ (nhưng tôi nghĩ câu trả lời 'timeout3' ở đây tốt hơn nhiều).
hệ thống PAUSE

2
Bất kỳ lý do để không sử dụng timeouttiện ích gnu ?
Chris Johnson

timeouttuyệt! bạn thậm chí có thể sử dụng với nhiều lệnh (tập lệnh nhiều dòng): stackoverflow.com/a/61888916/658497
Noam Manos

Câu trả lời:


149

Tôi nghĩ rằng đây chính xác là những gì bạn đang yêu cầu:

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

Đó là một mẹo nhỏ, sử dụng $$ cho phần nền. Và thật tuyệt khi nó sẽ giết chết một tlrbsf ngay lập tức. Nhưng ugh, bạn phải chọn một khoảng thời gian bỏ phiếu. Và nếu bạn đặt mức bỏ phiếu quá thấp, nó sẽ ăn CPU với tín hiệu liên tục, làm cho tlrbsf chạy lâu hơn nữa!
hệ thống PAUSE

7
Bạn không phải chọn khoảng thời gian bỏ phiếu, nó có mặc định là 1 giây, điều đó khá tốt. Và việc kiểm tra là rất rẻ, chi phí không đáng kể. Tôi nghi ngờ rằng sẽ làm cho tlrbsf chạy dài hơn đáng kể. Tôi đã thử nghiệm với giấc ngủ 30 và nhận được chênh lệch 0,000ms giữa việc sử dụng và không sử dụng nó.
Juliano

6
Phải, tôi thấy điều đó bây giờ. Và nó đáp ứng các yêu cầu chính xác của tôi nếu bạn đặt khoảng thời gian thăm dò ý kiến ​​== thời gian chờ. Cũng hoạt động trong các đường ống, hoạt động với toàn bộ nền, hoạt động với nhiều trường hợp và các công việc khác đang chạy. Lời cảm ơn ngọt ngào!
hệ thống PAUSE

Gửi một tín hiệu giết chết lớp vỏ phụ, vì vậy tôi nghĩ rằng việc lót tất cả các lệnh kill trên một dòng sẽ bảo toàn chúng. Tôi cũng kích hoạt đầu ra stderr để hiển thị các lỗi không mong muốn. stackoverflow.com/questions/687948/
Mạnh

1
@Juliano Đó là một cách tuyệt vời để xử lý thời gian chờ, rất hữu ích. Tôi tự hỏi liệu có cách nào để chúng ta có thể có đoạn mã trả về mã thoát 143 khi quá trình bị hủy sau khi hết thời gian không? Tôi đã thử thêm "exit 143" ngay sau lệnh kill, nhưng tôi luôn nhận được mã thoát 0 ở tập lệnh người gọi.
Salman A. Kagzi

528

Bạn có thể đang tìm kiếm timeoutlệnh trong coreutils. Vì nó là một phần của coreutils, về mặt kỹ thuật, đây là một giải pháp C, nhưng nó vẫn là coreutils. info timeoutđể biết thêm chi tiết. Đây là một ví dụ:

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

21
Trong Mac, bạn có thể cài đặt ứng dụng này thông qua Macports hoặc homebrew.
Ivan Z. Siu

23
Khi được cài đặt qua homebrew trên OS X, lệnh sẽ trở thànhgtimeout
ethicalhack3r

5
... Bạn đang sử dụng hệ điều hành nào có coreutils từ trước 2003?
Keith

5
@Keith: CentOS 5.10, ví dụ :-(
Tạm dừng cho đến khi có thông báo mới.

9
Để làm rõ, lệnh homebrew bạn cần trên OSX / mac là brew install coreutils, và sau đó bạn có thể sử dụng gtimeout.
Ohad Schneider

37

Giải pháp này hoạt động bất kể chế độ màn hình bash. Bạn có thể sử dụng tín hiệu thích hợp để chấm dứt 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

Trình theo dõi sẽ giết your_command sau khi hết thời gian; kịch bản chờ đợi cho nhiệm vụ chậm và chấm dứt người xem. Lưu ý rằng waitkhông hoạt động với các quy trình là con của một lớp vỏ khác.

Ví dụ:

  • your_command chạy hơn 2 giây và bị chấm dứt

your_command bị gián đoạn

( 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 kết thúc trước khi hết thời gian (20 giây)

your_command đã hoàn thành

( 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
waittrả về trạng thái thoát của quá trình mà nó chờ. Vì vậy, nếu lệnh của bạn thoát trong thời gian quy định nhưng với trạng thái thoát khác không thì logic ở đây sẽ hoạt động như đã hết thời gian, tức là in your_command interrupted. Thay vào đó, bạn có thể làm waitmà không cần ifvà sau đó kiểm tra xem $watcherpid có còn tồn tại không, nếu có thì bạn biết bạn đã không hết thời gian.
George Hawkins

Tôi không thể hiểu tại sao cái này sử dụng killtrong một trường hợp nhưng pkilltrong trường hợp khác. Tôi cần phải sử dụng pkillcho cả hai để làm cho công việc này chính xác. Tôi hy vọng rằng nếu bạn bọc một lệnh (), thì bạn sẽ cần phải sử dụng pkillđể giết nó. Nhưng có lẽ nó hoạt động khác nhau nếu chỉ có một lệnh duy nhất bên trong ().
tộc 9/2/2016

Chúa phù hộ bạn :)
Darko Miletic

22

Bạn đi đây

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

bạn có thể thay đổi SIGINT10như bạn mong muốn;)


3
"Hết giờ" là một phần của gói coreutils trên (ít nhất) Redhat, Centos, Suse và Ubuntu, vì vậy bạn sẽ cần cài đặt nó nếu bạn không có nó.
Akom

nó thực sự hữu ích !!!!!! Bạn có biết tại sao đôi khi "hết thời gian 5 / đường dẫn / đến / chậm / lệnh với các tùy chọn" của yingted không hoạt động không?
Decula

Thật không may, điều này không có trong coreutilsgói trên FreeBSD.
Paul Bissex

18

Bạn có thể làm điều này hoàn toàn với bash 4.3và trên:

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

  • Cần Bash 4.3 cho wait -n

  • Cung cấp cho 137 nếu lệnh bị giết, nếu không thì giá trị trả về của lệnh.
  • Công trình cho đường ống. (Bạn không cần phải đi trước ở đây!)
  • Hoạt động với các lệnh hoặc hàm shell bên trong, quá.
  • Chạy trong một subshell, vì vậy không xuất khẩu biến vào shell hiện tại, xin lỗi.

Nếu bạn không cần mã trả về, việc này có thể được thực hiện đơn giản hơn nữa:

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

Ghi chú:

  • Nói một cách chính xác, bạn không cần ;in ; ), tuy nhiên nó làm cho mọi thứ phù hợp hơn với chiếc ; }vali. Và set +bcó lẽ cũng có thể bị bỏ đi, nhưng an toàn tốt hơn là xin lỗi.

  • Ngoại trừ --forground(có thể) bạn có thể thực hiện tất cả các biến thể timeouthỗ trợ. --preserve-statuslà một chút khó khăn, mặc dù. Điều này được để lại như một bài tập cho người đọc;)

Công thức này có thể được sử dụng "tự nhiên" trong vỏ (tự nhiên như đối với flock fd):

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

Tuy nhiên, như đã giải thích ở trên, bạn không thể xuất lại các biến môi trường vào vỏ kèm theo cách này một cách tự nhiên.

Biên tập:

Ví dụ trong thế giới thực: Hết thời gian __git_ps1trong trường hợp mất quá nhiều thời gian (đối với những thứ như SSHFS-Links chậm):

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

Chỉnh sửa2: Sửa lỗi. Tôi nhận thấy rằng điều đó exit 137là không cần thiết và làm cho _timeoutkhông đáng tin cậy cùng một lúc.

Edit3: gitlà một die-hard, vì vậy nó cần một thủ thuật kép để làm việc thỏa mãn.

Edit4: Quên một _cái đầu tiên _timeoutcho ví dụ GIT trong thế giới thực.


1
Bash 4 tảng đá. Đó là tất cả.
hệ thống PAUSE

1
Điều này thực sự đòi hỏi Bash 4.3 hoặc mới hơn. cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status. Từ: tiswww.case.edu/php/chet/bash/NEWS
Ben Reser

17

Tôi thích "tim Friit", trong đó có một gói ít nhất là trong debian.

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

Nó đẹp hơn một chút so với "thời gian chờ" của coreutils vì nó in ra thứ gì đó khi giết tiến trình, và nó cũng gửi SIGKILL sau một thời gian theo mặc định.


Nó dường như không làm việc khá tốt: / $ thời gian thời hạn -T2 ngủ 10 thực 0m10.003s 0m0.000s dùng sys 0m0.000s
hithwen

3
Sử dụng -t2 không -T2. Lớn -T là thời gian từ khi gửi SIGTERM cho đến khi gửi SIGKILL.
MAXY

1
Tôi muốn thêm rằng timensonit 1.8 không có đường nối hoạt động tốt với ngã ba ( timelimit -t1 ./a_forking_progchỉ giết một trong hai quy trình), nhưng thời gian chờ hoạt động.
Jeremy Cochoy

Nếu bạn muốn hết thời gian để in một cái gì đó khi giết tiến trình, chỉ cần sử dụng cờ "-v".

10

Để hết thời gian slowcommandsau 1 giây:

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

Để xác định xem lệnh đã hết thời gian hay thất bại vì lý do riêng của nó, hãy kiểm tra xem mã trạng thái là 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

Lưu ý rằng khi trạng thái thoát là 124, bạn không biết liệu nó đã hết thời gian do timeoutlệnh của bạn hay liệu lệnh đó có bị chấm dứt do một số logic hết thời gian nội bộ của chính nó và sau đó trả về 124. Bạn có thể giả sử một cách an toàn trong cả hai trường hợp mặc dù, đó là một thời gian chờ của một số loại đã xảy ra.



8

Kinda hacky, nhưng nó hoạt động. Không hoạt động nếu bạn có các quy trình nền trước khác (vui lòng giúp tôi sửa lỗi này!)

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

Trên thực tế, tôi nghĩ bạn có thể đảo ngược nó, đáp ứng tiêu chí 'tiền thưởng' của bạn:

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

(ls -ltR / cygdrive / c / windows & SPID = $ {!}; (ngủ 1s; giết $ {SPID}) & CPID = $ {!}; fg 1; giết $ {CPID})> fdsa
PAUSE hệ thống

bash: fg: không kiểm soát công việc
hệ thống PAUSE

@system PAUSE, set -m, tôi nghĩ vậy.
strager

Tôi có kiểm soát công việc (set -m) trong vỏ đăng nhập. Đó là 'm' trong nội dung himBH của $ - nhưng dường như nó biến mất trong các subshells. Có thể là một cổ vật Cygwin. càu nhàu
hệ thống PAUSE

Đừng sử dụng "fg" trong tập lệnh. Đọc "giúp chờ".
lhunath

8

thời gian chờ có lẽ là cách tiếp cận đầu tiên để thử. Bạn có thể cần thông báo hoặc lệnh khác để thực thi nếu hết thời gian. Sau khi tìm kiếm và thử nghiệm khá nhiều, tôi đã tìm ra kịch bản bash này :

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

Kịch bản đơn giản với mã rõ ràng. Lưu vào /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

Lần ra một lệnh chạy quá lâu:

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

Kết thúc ngay lập tức cho một lệnh hoàn thành:

$ run 10 sleep 2
$

3

Nếu bạn đã biết tên của chương trình (giả sử program) chấm dứt sau khi hết thời gian (như một ví dụ 3giây), tôi có thể đóng góp một giải pháp thay thế đơn giản và hơi bẩn:

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

Điều này hoạt động hoàn hảo nếu tôi gọi các quy trình chuẩn với các cuộc gọi hệ thống.


2
Điều này giết chết các quá trình khác xảy ra để sử dụng tên và không giết quá trình với tên đã cho nếu nó thay đổi tên của nó (ví dụ bằng cách viết vào argv[0], có lẽ với các bản hack khác để tạo thêm khoảng trống).
Jed

Tôi đã tìm thấy một biến thể của tiện dụng này khi cố gắng dừng các container docker sau một khoảng thời gian nhất định. Máy khách docker dường như không chấp nhận TERM / INT / KILL theo cách thực sự sẽ ngăn chặn container đang được chạy ở phía trước. Vì vậy, đặt tên cho container và sử dụng (sleep 3 && docker stop <container>) &làm việc độc đáo. Cảm ơn!
Mat Schaffer

vâng, đó là bụi bẩn và hạn chế, nhưng có thể được cải thiện như: { sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"Bằng cách này, nó sẽ chỉ giết các công việc trong vỏ hiện tại.
tấn

2

Ngoài ra còn có cratimeoutMartin Cracauer (viết bằng C cho hệ thống Unix và 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'

"Đặc biệt, nó bảo tồn hành vi tín hiệu." Rất vui khi có lựa chọn đó!
hệ thống PAUSE

1

OS X chưa sử dụng bash 4, cũng không có / usr / bin / timeout, vì vậy đây là một chức năng hoạt động trên OS X mà không cần pha chế tại nhà hoặc macports tương tự như / usr / bin / timeout (dựa trên Tino câu trả lời). Xác thực tham số, trợ giúp, sử dụng và hỗ trợ cho các tín hiệu khác là một bài tập cho người đọc.

# 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

Trong 99% các trường hợp, câu trả lời là KHÔNG thực hiện bất kỳ logic hết thời gian nào. Timeout logic là ở gần bất kỳ tình huống một dấu hiệu cảnh báo màu đỏ mà một cái gì đó khác là sai và cần được cố định thay vì .

Là quá trình của bạn treo hoặc phá vỡ sau n giây đôi khi? Sau đó tìm hiểu tại sao và sửa nó.

Bên cạnh đó, để thực hiện đúng giải pháp của strager, bạn cần sử dụng chờ "$ SPID" thay vì fg 1, vì trong các tập lệnh bạn không có kiểm soát công việc (và cố gắng bật nó là ngu ngốc). Hơn nữa, fg 1 phụ thuộc vào thực tế là bạn đã không bắt đầu bất kỳ công việc nào khác trước đây trong kịch bản, đó là một giả định tồi để thực hiện.


4
Với quyền truy cập vào 100% nguồn (và hầu hết các phần cứng, chẳng hạn như chuyển mạch mạng), tôi đồng ý rằng có thể có các giải pháp tốt hơn thời gian chờ. Nhưng khi 'tlrbsf' là nguồn đóng, chỉ nhị phân, đôi khi bạn phải giải quyết giới hạn đó.
hệ thống PAUSE

@lhunath, "trong kịch bản bạn không có quyền kiểm soát công việc (và cố gắng để bật tính năng này là ngu ngốc)" - Hãy làm rõ ở đây: stackoverflow.com/questions/690266/...
hệ thống PAUSE

@system PAUSE: Trả lời stackoverflow.com/questions/690266/ trên là chính xác, tôi cũng nhận xét về nó.
lhunath

29
lhunath, những gì bạn đang nói không có ý nghĩa. có rất nhiều trường hợp trong đó thời gian chờ là một lựa chọn tốt, ví dụ như bất cứ lúc nào bạn phải truy cập mạng.
Nate Murray

Một ngoại lệ: bạn đang viết tập lệnh kiểm tra để kiểm tra xem phần mềm đã chạy chưa có vấn đề về thời gian chờ.
peterh - Phục hồi Monica

0

Tôi đã gặp phải một vấn đề để bảo vệ bối cảnh shell và cho phép hết thời gian chờ, vấn đề duy nhất là nó sẽ dừng thực thi tập lệnh khi hết thời gian - nhưng nó vẫn ổn với nhu cầu tôi đã trình bày:

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

với đầu ra:

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

Tất nhiên tôi cho rằng có một thư mục được gọi là 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 Xin lỗi tôi đã quên tại sao tôi thay đổi dòng kết thúc quá trình và tại sao tôi nghĩ rằng điều này là quan trọng để chia sẻ. Thật tệ là tôi đã không viết nó xuống. Có lẽ, tôi thấy rằng tôi cần phải tạm dừng trước khi kiểm tra sự thành công của kill -s TERM. Kịch bản năm 2008 từ sách nấu ăn dường như kiểm tra trạng thái của quy trình ngay sau khi gửi SIGTERM, có thể dẫn đến lỗi khi cố gửi SIGKILL đến một quy trình đã chết.
lươn ghEEz 3/03/2015

0

Vấn đề của tôi có thể hơi khác một chút: Tôi bắt đầu một lệnh thông qua ssh trên một máy từ xa và muốn giết shell và các con nếu lệnh bị treo.

Bây giờ tôi sử dụng như sau:

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

Bằng cách này, lệnh trả về 255 khi có thời gian chờ hoặc mã trả về của lệnh trong trường hợp thành công

Xin lưu ý rằng các quy trình tiêu diệt từ phiên ssh được xử lý khác với trình bao tương tác. Nhưng bạn cũng có thể sử dụng tùy chọn -t để ssh để phân bổ thiết bị đầu cuối giả, để nó hoạt động như một vỏ tương tác


0

Đây là một phiên bản không dựa vào việc sinh ra một tiến trình con - tôi cần một tập lệnh độc lập có chức năng này. Nó cũng thực hiện một khoảng thời gian thăm dò phân đoạn, vì vậy bạn có thể thăm dò nhanh hơn. thời gian chờ sẽ được ưu tiên hơn - nhưng tôi bị kẹt trên một máy chủ cũ

# 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

Một cách rất đơn giản:

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

với pkill (tùy chọn -f ), bạn có thể hủy lệnh cụ thể của mình bằng các đối số hoặc chỉ định -n để tránh hủy quy trình cũ.


Bạn nhận ra đây thực chất là những gì OP có trong bài đăng của anh ấy và những gì anh ấy cho biết anh ấy không muốn, phải không? Bởi vì nó luôn chờ đợi sự chậm trễ của giấc ngủ đầy đủ.
Etan Reisner

-1

Dựa trên câu trả lời của @ loup ...

Nếu bạn muốn hết thời gian xử lý và tắt đầu ra lệnh kill / pid, hãy chạy:

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

Điều này đặt quá trình nền thành một lớp con để bạn không thấy đầu ra công việc.


-3

Tôi có một công việc định kỳ gọi một tập lệnh php và đôi khi, nó bị kẹt trên tập lệnh php. Giải pháp này là hoàn hảo với tôi.

Tôi sử dụng:

scripttimeout -t 60 /script.php

1
scripttimeout
rudolfbyker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.