Làm cách nào để tập lệnh unix chạy cứ sau 15 giây?


84

Tôi đã thấy một số giải pháp, bao gồm xem và chỉ đơn giản là chạy một tập lệnh lặp (và ngủ) trong nền, nhưng không có gì là lý tưởng.

Tôi có một tập lệnh cần chạy 15 giây một lần và vì cron sẽ không hỗ trợ giây, tôi còn lại để tìm ra thứ khác.

Cách mạnh mẽ và hiệu quả nhất để chạy một tập lệnh cứ sau 15 giây trên unix là gì? Tập lệnh cũng cần chạy sau khi khởi động lại.


1
Mất bao lâu để chạy?
Adam Matan

Câu trả lời:


77

Tôi sẽ sử dụng cron để chạy một tập lệnh mỗi phút và làm cho tập lệnh đó chạy tập lệnh của bạn bốn lần với thời gian nghỉ 15 giây giữa các lần chạy.

(Điều đó giả định rằng tập lệnh của bạn chạy nhanh - bạn có thể điều chỉnh thời gian ngủ nếu không.)

Bằng cách đó, bạn nhận được tất cả các lợi ích croncũng như khoảng thời gian chạy 15 giây của mình.

Chỉnh sửa: Xem thêm bình luận của @ bmb bên dưới.


@Aiden: Ha! Kẻ thù không đội trời chung của tôi, chúng ta lại gặp nhau!
RichieHindle

56
Nếu tập lệnh không nhất quán về thời gian chạy, hãy tạo bốn bản sao của tập lệnh. Một người ngủ 15 giây trước khi bắt đầu, 30 giây khác, 45 giây khác và một số 0 khác. Sau đó chạy tất cả bốn phút mỗi phút.
bmb

@RichieHindle - Đừng sợ, tôi đã bị ám sát vì không tính từng phút thành giây. Nhưng tôi đang theo dõi bạn: P
Aiden Bell

Làm thế nào có thể được cron này được kích hoạt mỗi 1 phút
DevZer0

Trên thực tế, kịch bản bên ngoài nên chạy tập lệnh bên trong ba lần, không phải bốn. Nếu không lần chạy cuối cùng của phút trước sẽ chồng lên lần chạy của phút tiếp theo. Điều đó sẽ là chạy script bên trong 5 lần mỗi phút thay vì bốn lần.
Tulains Córdova

290

Nếu bạn khăng khăng chạy tập lệnh của mình từ cron:

* * * * * /foo/bar/your_script
* * * * * sleep 15; /foo/bar/your_script
* * * * * sleep 30; /foo/bar/your_script
* * * * * sleep 45; /foo/bar/your_script

và thay thế tên tập lệnh và đường dẫn của bạn thành / foo / bar / your_script


18
điều này là rất thông minh. +1
SingleNegationElimination

4
Điều này làm việc hoàn hảo cho tôi. Sự giải quyết ở trên này khi sử dụng một tác vụ nền đã tạo ra một số tiến trình con và gây ra các vấn đề về bộ nhớ cho tôi.
Hàng đêm

2
nếu chạy php script làm điều này:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign

1
nếu chạy php script bạn có thể thêm vào trước dòng #!/usr/bin/phptrên cùng của kịch bản php của bạn và làm cho nó thực thi
Aaron Blenkush

18
Tôi cảm thấy xấu hổ rằng tôi đã phải google cho giải pháp này. Có lẽ stackoverflow khiến tôi ít suy nghĩ hơn.
chris finne

15

Phiên bản sửa đổi ở trên:

mkdir /etc/cron.15sec
mkdir /etc/cron.minute
mkdir /etc/cron.5minute

thêm vào / etc / crontab:

* * * * * root run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 15; run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 30; run-parts /etc/cron.15sec > /dev/null 2> /dev/null
* * * * * root sleep 45; run-parts /etc/cron.15sec > /dev/null 2> /dev/null

* * * * * root run-parts /etc/cron.minute > /dev/null 2> /dev/null
*/5 * * * * root run-parts /etc/cron.5minute > /dev/null 2> /dev/null

13

Không chạy trong nền có làm được không?

#!/bin/sh
while [ 1 ]; do
    echo "Hell yeah!" &
    sleep 15
done

Điều này là hiệu quả như nó nhận được. Phần quan trọng chỉ được thực thi sau mỗi 15 giây và tập lệnh sẽ ngủ trong thời gian còn lại (do đó không lãng phí chu kỳ).


8
Các chỉnh sửa phải có ít nhất 8 ký tự (không phải là IMHO) vì vậy tôi không thể thêm ký tự &vào cuối dòng 3. Trong mọi trường hợp, như vậy, điều này không chạy cứ sau 15 giây. Điều này sẽ chạy sau mỗi "15 giây + tuy nhiên echo hellomất nhiều thời gian để chạy". Có thể là 0,01 giây; có thể là 19 giờ.
Parthian Shot

1

Tôi đã viết một công cụ lập lịch nhanh hơn cron. Tôi cũng đã thực hiện một bảo vệ chồng chéo. Bạn có thể định cấu hình bộ lập lịch để không bắt đầu quá trình mới nếu quá trình trước đó vẫn đang chạy. Hãy xem tại https://github.com/sioux1977/scheduler/wiki


0

Sử dụng nanosleep (2) . Nó sử dụng cấu trúc timespecdùng để chỉ định khoảng thời gian với độ chính xác nano giây.

struct timespec {
           time_t tv_sec;        /* seconds */
           long   tv_nsec;       /* nanoseconds */
       };

1
Tôi sẽ tiếp tục và đoán rằng chúng không cần độ chính xác nano giây, vì thứ chúng tạo ra sau mỗi 15 giây là một tập lệnh shell, không phải một chuỗi hạt nhân.
Parthian Shot,

@ParthianShot có thể nhưng bạn chưa bao giờ biết.
Alexander Suraphel

0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done

0

Kể từ câu trả lời trước đây của tôi, tôi đã đưa ra một giải pháp khác khác và có lẽ tốt hơn. Mã này cho phép các quy trình được chạy hơn 60 lần một phút với độ chính xác micro giây. Bạn cần chương trình usleep để làm việc này. Nên tốt lên đến 50 lần một giây.

#! /bin/sh

# Microsecond Cron
# Usage: cron-ms start
# Copyright 2014 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

basedir=/etc/cron-ms

if [ $# -eq 0 ]
then
   echo
   echo "cron-ms by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel every X times per minute."
   echo "Think of this program as cron with microseconds resolution."
   echo
   echo "Usage: cron-ms start"
   echo
   echo "The scheduling is done by creating directories with the number of"
   echo "executions per minute as part of the directory name."
   echo
   echo "Examples:"
   echo "  /etc/cron-ms/7      # Executes everything in that directory  7 times a minute"
   echo "  /etc/cron-ms/30     # Executes everything in that directory 30 times a minute"
   echo "  /etc/cron-ms/600    # Executes everything in that directory 10 times a second"
   echo "  /etc/cron-ms/2400   # Executes everything in that directory 40 times a second"
   echo
   exit
fi

# If "start" is passed as a parameter then run all the loops in parallel
# The number of the directory is the number of executions per minute
# Since cron isn't accurate we need to start at top of next minute

if [ $1 = start ]
then
   for dir in $basedir/* ; do
      $0 ${dir##*/} 60000000 &
   done
   exit
fi

# Loops per minute and the next interval are passed on the command line with each loop

loops=$1
next_interval=$2

# Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute

usleep $(( $next_interval - 10#$(date +%S%N) / 1000 ))

# Run all the programs in the directory in parallel

for program in $basedir/$loops/* ; do
   if [ -x $program ] 
   then
      $program &> /dev/null &
   fi
done

# Calculate next_interval

next_interval=$(($next_interval % 60000000 + (60000000 / $loops) ))

# If minute is not up - call self recursively

if [ $next_interval -lt $(( 60000000 / $loops * $loops)) ]
then
   . $0 $loops $next_interval &
fi

# Otherwise we're done

1
Chỉnh sửa bản gốc thay vì đăng lại!
Vogon Jeltz 20/02/16

-1

Để tránh chồng chéo thực thi có thể xảy ra, hãy sử dụng cơ chế khóa như được mô tả trong luồng đó .


2
-1 OP không nói rằng anh ta cần tránh thực hiện chồng chéo; điều này có thể được tái sử dụng. Thêm vào đó, điều này không trả lời câu hỏi.
Parthian Shot,
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.