Timeout perintah dalam bash tanpa penundaan yang tidak perlu

283

Jawaban untuk perintah baris perintah ini untuk secara otomatis membunuh perintah setelah waktu tertentu

mengusulkan metode 1-baris untuk menghentikan perintah yang sudah berjalan lama dari baris perintah bash:

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

Tapi mungkin saja perintah "jangka panjang" yang diberikan bisa selesai lebih awal dari batas waktu. (Sebut saja "perintah yang biasanya berjalan lama tapi kadang-kadang cepat", atau tlrbsf untuk bersenang-senang.)

Jadi pendekatan 1-liner yang bagus ini memiliki beberapa masalah. Pertama, sleeptidak bersyarat, sehingga menetapkan batas bawah yang tidak diinginkan pada waktu yang dibutuhkan untuk menyelesaikan urutan. Pertimbangkan 30s atau 2m atau bahkan 5m untuk tidur, ketika perintah tlrbsf selesai dalam 2 detik - sangat tidak diinginkan. Kedua, killtidak bersyarat, sehingga urutan ini akan mencoba untuk membunuh proses yang tidak berjalan dan mengeluh tentang hal itu.

Begitu...

Apakah ada cara untuk menghentikan perintah ( "tlrbsf" ) yang biasanya berjalan lama tapi kadang-kadang cepat

  • memiliki implementasi bash (pertanyaan lain sudah memiliki jawaban Perl dan C)
  • akan berakhir di bagian paling awal dari keduanya: penghentian program tlrbsf , atau batas waktu telah berlalu
  • tidak akan membunuh proses yang tidak ada / tidak berjalan (atau, opsional: tidak akan mengeluh tentang pembunuhan yang buruk)
  • tidak harus menjadi 1-liner
  • dapat berjalan di bawah Cygwin atau Linux

... dan, untuk poin bonus, jalankan perintah tlrbsf di latar depan dan 'sleep' atau proses tambahan apa pun di latar belakang, sehingga stdin / stdout / stderr dari perintah tlrbsf dapat dialihkan, sama seperti jika telah lari langsung?

Jika demikian, silakan bagikan kode Anda. Jika tidak, tolong jelaskan mengapa.

Saya telah menghabiskan beberapa waktu mencoba meretas contoh yang disebutkan di atas tetapi saya mencapai batas kemampuan bash saya.

sistem PAUSE
sumber
5
Pertanyaan serupa lainnya: stackoverflow.com/questions/526782/… (tapi saya pikir jawaban 'timeout3' di sini jauh lebih baik).
system PAUSE
2
Ada alasan untuk tidak menggunakan timeoututilitas gnu ?
Chris Johnson
timeoutbagus! Anda bahkan dapat menggunakan beberapa perintah (skrip multi-baris): stackoverflow.com/a/61888916/658497
Noam Manos

Jawaban:

149

Saya pikir inilah tepatnya yang Anda minta:

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 "$@"
Juliano
sumber
Itu trik yang tajam, menggunakan $$ untuk bagian latar belakang. Dan bagus bahwa itu akan membunuh tlrbsf yang digantung segera. Tapi huh, kamu harus memilih jajak pendapat. Dan jika Anda mengatur polling terlalu rendah, itu akan memakan CPU dengan pensinyalan konstan, membuat tlrbsf berjalan lebih lama!
system PAUSE
7
Anda tidak harus memilih interval polling, ia memiliki default 1s, itu cukup bagus. Dan pemeriksaannya sangat murah, biaya overhead bisa diabaikan. Saya ragu itu akan membuat tlrbsf berjalan lebih lama. Saya diuji dengan sleep 30, dan mendapat perbedaan 0,000ms antara menggunakan dan tidak menggunakannya.
Juliano
6
Benar, saya melihat itu sekarang. Dan itu memenuhi persyaratan tepat saya jika Anda mengatur interval polling == batas waktu. Juga bekerja di jalur pipa, bekerja dengan seluruh latar belakang, bekerja dengan banyak instance dan pekerjaan lain berjalan. Manis terima kasih
system PAUSE
Mengirim sinyal membunuh sub-shell, jadi saya berpikir bahwa melapisi semua perintah kill pada satu baris akan mempertahankannya. Saya juga mengaktifkan stderr output untuk menampilkan kesalahan yang tidak terduga. stackoverflow.com/questions/687948/…
eel ghEEz
1
@Juliano Itu cara yang bagus untuk menangani timeout, sangat berguna. Saya bertanya-tanya apakah ada cara di mana kita dapat memiliki kode kembali kode sandi 143 ketika proses ini dibunuh setelah batas waktu? Saya mencoba menambahkan "keluar 143" tepat setelah perintah kill, tapi saya selalu mendapatkan kode keluar 0 pada skrip pemanggil.
Salman A. Kagzi
528

Anda mungkin mencari timeoutperintah di coreutils. Karena ini adalah bagian dari coreutils, secara teknis ini adalah solusi C, tetapi masih coreutils. info timeoutuntuk lebih jelasnya. Ini sebuah contoh:

timeout 5 /path/to/slow/command with options
yingted
sumber
21
Di Mac Anda dapat menginstal ini melalui Macports atau homebrew.
Ivan Z. Siu
23
Ketika diinstal melalui homebrew pada OS X, perintahnya menjadigtimeout
ethichack3r
5
... OS apa yang Anda gunakan yang memiliki coreutils sebelum 2003?
Keith
5
@Keith: CentOS 5.10, misalnya :-(
Dijeda hingga pemberitahuan lebih lanjut.
9
Untuk memperjelas, perintah homebrew yang Anda butuhkan pada OSX / mac adalah brew install coreutils, dan kemudian Anda dapat menggunakannya gtimeout.
Ohad Schneider
37

Solusi ini berfungsi terlepas dari mode bash monitor. Anda dapat menggunakan sinyal yang tepat untuk mengakhiri perintah_Anda

#!/bin/sh
( your_command ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

Pengamat membunuh perintah_Anda setelah batas waktu diberikan; skrip menunggu tugas yang lambat dan menghentikan pengamat. Catat ituwait tidak bekerja dengan proses yang merupakan anak-anak dari shell yang berbeda.

Contoh:

  • your_command berjalan lebih dari 2 detik dan dihentikan

perintah_Anda terputus

( 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
  • perintah_Anda selesai sebelum batas waktu (20 detik)

perintah your_ selesai

( 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
Dmitry
sumber
1
waitmengembalikan status keluar dari proses yang ditunggu. Jadi, jika perintah Anda benar-benar keluar dalam waktu yang ditentukan tetapi dengan status keluar bukan nol maka logika di sini akan berperilaku seperti waktu habis, yaitu cetak your_command interrupted. Sebaliknya Anda bisa melakukan waittanpa ifdan kemudian memeriksa apakah $watcherpid masih ada, jika tidak maka Anda tahu Anda tidak kehabisan waktu.
George Hawkins
Saya tidak tahu mengapa ini digunakan killdalam satu kasus tetapi pkilldalam kasus lain. Saya perlu menggunakan pkillkeduanya untuk membuat ini berfungsi dengan benar. Saya berharap bahwa jika Anda memasukkan perintah (), maka Anda harus menggunakan pkilluntuk membunuhnya. Tetapi mungkin ini bekerja secara berbeda jika hanya ada satu perintah di dalam ().
nobar
Tuhan memberkati Anda, Sir :)
Darko Miletic
22

Ini dia:

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

Anda dapat mengubah SIGINTdan 10sesuai keinginan;)

Aarian P. Aleahmad
sumber
3
"timeout" adalah bagian dari paket coreutils di (setidaknya) Redhat, Centos, Suse dan Ubuntu, jadi Anda harus menginstalnya jika Anda tidak memilikinya.
Akom
ini sangat membantu !!!!!! Apakah Anda tahu mengapa perintah "timeout 5 / path / to / slow / yingted dengan opsi" terkadang tidak berfungsi?
Decula
Sayangnya ini tidak ada dalam coreutilspaket di FreeBSD.
Paul Bissex
18

Anda dapat melakukan ini sepenuhnya dengan bash 4.3dan di atas:

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

  • Membutuhkan Bash 4.3 untuk wait -n

  • Memberi 137 jika perintah itu terbunuh, atau nilai kembalinya dari perintah itu.
  • Bekerja untuk pipa. (Anda tidak perlu pergi lebih dulu ke sini!)
  • Bekerja dengan perintah atau fungsi shell internal juga.
  • Berjalan dalam subkulit, jadi tidak ada variabel ekspor ke shell saat ini, maaf.

Jika Anda tidak memerlukan kode pengembalian, ini dapat dibuat lebih sederhana:

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

Catatan:

  • Sebenarnya Anda tidak perlu ;masuk ; ), tetapi itu membuat hal lebih konsisten ke- ; }tas. Dan set +bmungkin bisa dibiarkan begitu saja, tetapi lebih baik aman daripada menyesal.

  • Kecuali untuk --forground(mungkin) Anda dapat mengimplementasikan semua varian timeoutdukungan. --preserve-statusmemang agak sulit. Ini dibiarkan sebagai latihan untuk pembaca;)

Resep ini dapat digunakan "secara alami" dalam cangkang (sealami untuk flock fd):

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

Namun, seperti dijelaskan di atas, Anda tidak dapat mengekspor kembali variabel lingkungan ke shell penutup dengan cara ini secara alami.

Edit:

Contoh dunia nyata: Waktu habis __git_ps1jika terlalu lama (untuk hal-hal seperti SSHFS-Links lambat):

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

Edit2: Perbaikan Bug. Saya perhatikan itu exit 137tidak diperlukan dan membuat_timeout tidak dapat diandalkan pada saat yang sama.

Sunting3: git sangat sulit, sehingga perlu trik ganda untuk bekerja dengan memuaskan.

Sunting4: Lupa a _di pertama _timeoutuntuk contoh GIT dunia nyata.

Tino
sumber
1
Bash 4 batu. Itu semuanya.
system PAUSE
1
Ini sebenarnya membutuhkan Bash 4.3 atau lebih baru. cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status. Dari: tiswww.case.edu/php/chet/bash/NEWS
Ben Reser
17

Saya lebih suka "timelimit", yang memiliki paket setidaknya di debian.

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

Ini sedikit lebih bagus daripada "timeout" coreutils karena ia mencetak sesuatu ketika mematikan proses, dan juga mengirimkan SIGKILL setelah beberapa waktu secara default.

maxy
sumber
Tampaknya tidak berfungsi dengan baik: / $ waktu timelimit -T2 tidur 10 nyata 0m10,003s pengguna 0m0,000s sys 0m0,000s
hithwen
3
Gunakan -t2 bukan -T2. -T besar adalah waktu dari mengirim SIGTERM hingga mengirim SIGKILL.
maksimal
1
Saya ingin menambahkan bahwa timelimit 1.8 tidak bekerja dengan baik dengan fork ( timelimit -t1 ./a_forking_proghanya membunuh satu dari dua proses), tetapi timeout bekerja.
Jeremy Cochoy
Jika Anda ingin waktu habis untuk mencetak sesuatu saat mematikan proses, cukup gunakan bendera "-v".
10

Untuk batas waktu slowcommandsetelah 1 detik:

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

Untuk menentukan apakah perintah habis atau gagal karena alasannya sendiri, periksa apakah kode statusnya 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

Perhatikan bahwa ketika status keluar adalah 124, Anda tidak tahu apakah itu habis karena timeoutperintah Anda , atau apakah perintah itu sendiri diakhiri karena beberapa logika batas waktu internal sendiri dan kemudian dikembalikan 124. Anda dapat dengan aman berasumsi dalam kedua kasus Namun, batas waktu semacam itu terjadi.

tombak
sumber
9

Lihat juga skrip http://www.pixelbeat.org/scripts/timeout yang fungsinya telah diintegrasikan ke dalam coreutils yang lebih baru

pixelbeat
sumber
Rapi, sederhana, menggunakan TERM dan tidak MEMBUNUH. Bagus! Saya telah menjelajahi solusi jebakan / tunggu seperti ini ketika saya awalnya mengajukan pertanyaan.
system PAUSE
batas waktu kembali 124 jika terjadi pembunuhan.
ceving
8

Agak hacky, tetapi berhasil. Tidak berfungsi jika Anda memiliki proses latar depan lainnya (tolong bantu saya memperbaiki ini!)

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

Sebenarnya, saya pikir Anda dapat membalikkannya, memenuhi kriteria 'bonus' Anda:

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa
strager
sumber
(ls -ltR / cygdrive / c / windows & SPID = $ {!}; (sleep 1s; kill $ {SPID}) & CPID = $ {!}; fg 1; bunuh $ {CPID})> fdsa
system PAUSE
bash: fg: tidak ada kontrol pekerjaan
sistem PAUSE
@SUSE PAUSE, set -m, saya pikir.
strager
Saya memiliki kontrol pekerjaan (set -m) di shell login. Itulah 'm' di konten himBH $ -, tetapi tampaknya menghilang untuk subkulit. Mungkin artefak Cygwin. grumble
system PAUSE
Jangan gunakan "fg" dalam skrip. Baca "tolong tunggu".
lhunath
8

batas waktu mungkin merupakan pendekatan pertama untuk mencoba. Anda mungkin perlu pemberitahuan atau perintah lain untuk mengeksekusi jika itu habis. Setelah sedikit mencari dan bereksperimen, saya membuat skrip bash ini :

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
pengguna2099484
sumber
5

Script sederhana dengan kejelasan kode. Simpan ke /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

Perintah time out yang berjalan terlalu lama:

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

Segera berakhir untuk perintah yang melengkapi:

$ run 10 sleep 2
$

sumber
3

Jika Anda sudah tahu nama program (mari kita asumsikan program) untuk berakhir setelah batas waktu (sebagai contoh 3detik), saya dapat berkontribusi solusi alternatif yang sederhana dan agak kotor:

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

Ini berfungsi dengan baik jika saya memanggil proses benchmark dengan panggilan sistem.

loup
sumber
2
Ini membunuh proses lain yang kebetulan menggunakan nama dan tidak membunuh proses dengan nama yang diberikan jika itu mengubah namanya (misalnya dengan menulis argv[0], mungkin dengan peretasan lain untuk membuat lebih banyak ruang).
Jed
Saya menemukan variasi ini berguna ketika mencoba untuk menghentikan wadah buruh pelabuhan setelah waktu tertentu. Klien buruh pelabuhan tampaknya tidak menerima TERM / INT / KILL dengan cara yang benar-benar akan menghentikan kontainer yang sedang berjalan di latar depan. Jadi memberi nama dan menggunakan wadah (sleep 3 && docker stop <container>) &bekerja dengan baik. Terima kasih!
Mat Schaffer
ya itu kotoran dan terbatas, tetapi dapat ditingkatkan seperti: { sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"Dengan cara ini, itu akan membunuh hanya pekerjaan di shell saat ini.
ton
2

Ada juga cratimeoutoleh Martin Cracauer (ditulis dalam C untuk Unix dan sistem 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'
maks .32
sumber
"Secara khusus, ini menjaga perilaku sinyal." Senang memiliki opsi itu!
system PAUSE
1

OS X belum menggunakan bash 4, juga tidak memiliki / usr / bin / timeout, jadi inilah fungsi yang bekerja pada OS X tanpa home-brew atau macports yang mirip dengan / usr / bin / timeout (berdasarkan Tino menjawab). Validasi parameter, bantuan, penggunaan, dan dukungan untuk sinyal lain adalah latihan untuk pembaca.

# 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
) }
NerdMachine
sumber
0

Dalam 99% kasus jawabannya adalah BUKAN untuk menerapkan logika batas waktu. Timeout logika dalam hampir setiap situasi tanda peringatan merah bahwa sesuatu yang lain yang salah dan harus diperbaiki bukan .

Apakah proses Anda tergantung atau rusak setelah n detik kadang-kadang? Kemudian cari tahu mengapa dan perbaiki itu sebagai gantinya.

Selain itu, untuk melakukan solusi strager dengan benar, Anda harus menggunakan tunggu "$ SPID" alih-alih fg 1, karena dalam skrip Anda tidak memiliki kontrol pekerjaan (dan mencoba menyalakannya itu bodoh). Selain itu, fg 1 bergantung pada fakta bahwa Anda tidak memulai pekerjaan lain sebelumnya dalam skrip yang merupakan asumsi buruk untuk dibuat.

lununath
sumber
4
Dengan akses ke 100% dari sumber (dan sebagian besar perangkat keras, seperti switch jaringan), saya akan setuju bahwa mungkin ada solusi yang lebih baik daripada batas waktu. Tetapi ketika 'tlrbsf' adalah sumber tertutup, hanya biner, kadang-kadang Anda harus mengatasi keterbatasan itu.
system PAUSE
@ lhunath, "dalam skrip Anda tidak memiliki kontrol pekerjaan (dan mencoba untuk menyalakannya adalah bodoh)" - Tolong jelaskan di sini: stackoverflow.com/questions/690266/…
system PAUSE
@sistem PAUSE: Balas stackoverflow.com/questions/690266/… benar, saya juga mengomentarinya.
lhunath
29
lhunath, apa yang kamu katakan tidak masuk akal. ada banyak kasus di mana pengaturan waktu adalah pilihan yang baik, misalnya kapan saja Anda harus pergi ke jaringan.
Nate Murray
Pengecualian: Anda menulis skrip pengujian untuk memeriksa apakah perangkat lunak yang sudah berjalan tidak memiliki masalah batas waktu.
peterh
0

Saya dihadapkan dengan masalah untuk mempertahankan konteks shell dan memungkinkan timeout, satu-satunya masalah dengan itu adalah akan menghentikan eksekusi skrip pada timeout - tetapi tidak masalah dengan kebutuhan saya disajikan:

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

dengan output:

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

tentu saja saya menganggap ada dir yang dipanggil scripts

mpapis
sumber
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 "$@"
belut ghEEz
sumber
@Tino Maaf saya lupa mengapa saya mengubah jalur terminasi proses dan mengapa saya pikir ini penting untuk dibagikan. Sayang sekali saya tidak menuliskan ini. Mungkin, saya menemukan bahwa saya perlu berhenti sebelum memeriksa keberhasilan kill -s TERM. Skrip tahun 2008 dari buku masak tampaknya memeriksa keadaan proses segera setelah mengirim SIGTERM, mungkin mengarah ke kesalahan mencoba mengirim SIGKILL ke proses yang mati.
belut ghEEz
0

Masalah saya mungkin sedikit berbeda: Saya memulai perintah melalui ssh pada mesin jarak jauh dan ingin membunuh shell dan childs jika perintah hang.

Saya sekarang menggunakan yang berikut ini:

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

Dengan cara ini perintah mengembalikan 255 ketika ada batas waktu atau kode pengembalian perintah jika berhasil

Harap dicatat bahwa proses mematikan dari sesi ssh ditangani berbeda dari shell interaktif. Tetapi Anda juga dapat menggunakan opsi -t untuk ssh untuk mengalokasikan terminal pseudo, sehingga berfungsi seperti shell interaktif

Franky_GT
sumber
0

Ini adalah versi yang tidak bergantung pada proses melahirkan anak - saya membutuhkan skrip mandiri yang menyematkan fungsi ini. Ini juga melakukan jajak pendapat fraksional, sehingga Anda dapat memilih lebih cepat. batas waktu akan lebih disukai - tapi saya terjebak di server lama

# 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
Neil McGill
sumber
-1

Cara yang sangat sederhana:

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

dengan pkill (opsi -f ) Anda dapat membunuh perintah spesifik Anda dengan argumen atau menentukan -n untuk menghindari membunuh proses lama.

Francis
sumber
Anda menyadari ini pada dasarnya apa yang dimiliki OP di posnya dan apa yang dia mengindikasikan tidak dia inginkan, bukan? Karena selalu menunggu waktu tidur yang lama.
Etan Reisner
-1

Membangun jawaban @ loup ...

Jika Anda ingin menghentikan proses dan mematikan output kill / pid, jalankan:

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

Ini menempatkan proses latar belakang ke dalam subkulit sehingga Anda tidak melihat output pekerjaan.

Cody A. Ray
sumber
-3

Saya memiliki pekerjaan cron yang memanggil skrip php dan, kadang-kadang, macet di skrip php. Solusi ini sempurna bagi saya.

Saya menggunakan:

scripttimeout -t 60 /script.php
Jehad Buria
sumber
1
Apa scripttimeout?
rudolfbyker