Làm thế nào để giới thiệu thời gian chờ cho kịch bản shell?


35

Tôi muốn chạy một kịch bản shell có một vòng lặp trong đó và nó có thể đi mãi mãi mà tôi không muốn xảy ra. Vì vậy, tôi cần phải giới thiệu một thời gian chờ cho toàn bộ kịch bản.

Làm cách nào tôi có thể giới thiệu thời gian chờ cho toàn bộ tập lệnh shell trong SuSE?


4
Đây là một câu hỏi rất mơ hồ. Bạn có thể vui lòng giải thích về loại thời gian chờ? Bạn có muốn thời gian chờ trên toàn bộ tập lệnh hoặc thời gian chờ trên một chức năng hoặc lệnh không? Bạn đang cố làm gì vậy?
Tim Kennedy

@TimKennedy: hy vọng nó tốt hơn bây giờ.
Radek

1
Xem thêm Thời gian chờ trong tập lệnh shell (Tôi không coi đó là bản sao vì yêu cầu về tính di động của tôi loại trừ các công cụ như timeouthoặc expect)
Gilles 'SO- ngừng trở thành ác quỷ'

Câu trả lời:


30

Nếu GNU timeoutkhông có sẵn, bạn có thể sử dụng expect(Mac OS X, BSD, ... thường không có các công cụ và tiện ích GNU theo mặc định).

################################################################################
# Executes command with a timeout
# Params:
#   $1 timeout in seconds
#   $2 command
# Returns 1 if timed out 0 otherwise
timeout() {

    time=$1

    # start the command in a subshell to avoid problem with pipes
    # (spawn accepts one command)
    command="/bin/sh -c \"$2\""

    expect -c "set echo \"-noecho\"; set timeout $time; spawn -noecho $command; expect timeout { exit 1 } eof { exit 0 }"    

    if [ $? = 1 ] ; then
        echo "Timeout after ${time} seconds"
    fi

}

Chỉnh sửa ví dụ:

timeout 10 "ls ${HOME}"

Có vẻ tốt. Làm thế nào tôi có thể thấy đầu ra của lệnh được thực hiện trên màn hình?
Radek

Xin chào, đầu ra sẽ hiển thị vì thiết bị xuất chuẩn không được chuyển hướng ở bất cứ đâu. Tôi đã thêm một ví dụ cho việc sử dụng. Trong trường hợp của tôi, nó in nội dung của thư mục nhà của tôi như mong đợi. Lệnh nào không được in bất kỳ đầu ra nào?
Matteo

Tôi đã không gọi nó là đúng. Nó hoạt động chỉ là tuyệt vời! Cảm ơn bạn. Chỉ có điều nó không xuất ra số giây sau khi hết thời gian.
Radek

@Radek Xin lỗi, đã sửa
Matteo

1
Tôi muốn nhấn mạnh rằng nếu sử dụng thời gian chờ GNU, trạng thái trả về của timeoutchính nó là 124 trong trường hợp hết thời gian chờ, nếu không thì đó là trạng thái trả về của lệnh (điều này được đề cập trong câu trả lời của Tim Kennedy).
QuasarDonkey

15

Cảm ơn bạn đã làm rõ.

Cách dễ nhất để thực hiện những gì bạn đang theo đuổi là chạy tập lệnh của bạn với vòng lặp trong trình bao bọc như timeoutlệnh từ gói GNU Coreutils.

root@coraid-sp:~# timeout --help            
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
   or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.

Mandatory arguments to long options are mandatory for short options too.
  -k, --kill-after=DURATION
                   also send a KILL signal if COMMAND is still running
                   this long after the initial signal was sent.
  -s, --signal=SIGNAL
                   specify the signal to be sent on timeout.
                   SIGNAL may be a name like 'HUP' or a number.
                   See `kill -l` for a list of signals
      --help     display this help and exit
      --version  output version information and exit

DURATION is an integer with an optional suffix:
`s' for seconds(the default), `m' for minutes, `h' for hours or `d' for days.

If the command times out, then exit with status 124.  Otherwise, exit
with the status of COMMAND.  If no signal is specified, send the TERM
signal upon timeout.  The TERM signal kills any process that does not
block or catch that signal.  For other processes, it may be necessary to
use the KILL (9) signal, since this signal cannot be caught.

Report timeout bugs to bug-coreutils@gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils 'timeout invocation'

Cuối cùng, nó sẽ dễ dàng hơn nhiều so với việc viết hàm thời gian chờ của riêng bạn, mà các shell có xu hướng không có nội dung.


Tôi đã không nhận ra ngay rằng tôi đã cài đặt nó, bởi vì nó được gọi là gtimeoutthiết lập của tôi (OSX).
Joshua Goldberg

7

Khởi chạy một quy trình theo dõi từ trong tập lệnh của bạn để tiêu diệt cha mẹ của nó nếu nó chạy quá lâu. Thí dụ:

# watchdog process
mainpid=$$
(sleep 5; kill $mainpid) &
watchdogpid=$!

# rest of script
while :
do
   ...stuff...
done
kill $watchdogpid

Kịch bản này sẽ bị chấm dứt bởi cơ quan giám sát sau năm giây.


6
Nhưng bạn sẽ phải chờ thời gian chờ mỗi lần. Trong ví dụ của bạn, tập lệnh của bạn sẽ luôn chạy 5 giây ngay cả khi tập lệnh ngắn hơn nhiều. Bạn cũng nên lưu trữ PID của cơ quan giám sát và tiêu diệt nó ở cuối.
Matteo

@Matteo Hội chợ đủ rồi. Thêm.
Kyle Jones

1
Lưu ý rằng nó có thể phức tạp hơn thế, tùy thuộc vào việc ... công cụ ... đang làm gì. Xem hết thời gian trong một kịch bản shell
Gilles 'SO- ngừng trở nên xấu xa'

đọc thêm: bao lâu các PID có thể được coi là stackoverflow.com/questions/11323410/linux-pid-recycling
Florian Castellane

3

Cũng có cratimeoutMartin Cracauer.

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

2
#!/bin/sh

# Execute a command with a timeout

if [ "$#" -lt "2" ]; then
echo "Usage:   `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi

cleanup()
{
trap - ALRM               #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null &&    #kill last job
  exit 124                #exit with 124 if it was running
}

watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}

watchit $1& a=$!         #start the timeout
shift                    #first param was timeout for sleep
trap "cleanup" ALRM INT  #cleanup after timeout
"$@"& wait $!; RET=$?    #start the job wait for it and save its return value
kill -ALRM $a            #send ALRM signal to watchit
wait $a                  #wait for watchit to finish cleanup
exit $RET                #return the value

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.