如何使Unix脚本每15秒运行一次?


84

我已经看到了一些解决方案,包括观看和在后台简单地运行循环(和休眠)脚本,但是没有什么是理想的。

我有一个脚本,该脚本需要每15秒运行一次,并且由于cron不支持秒,所以我不得不找出其他问题。

在UNIX上每15秒运行一次脚本的最可靠,最有效的方法是什么?重新启动后,脚本也需要运行。


1
运行需要多长时间?
亚当·马坦

Answers:


77

我会使用cron每分钟运行一个脚本,并使该脚本运行四次,两次运行之间的睡眠时间为15秒。

(假设您的脚本可以快速运行-如果没有,您可以调整睡眠时间。)

这样,您将获得cron15秒运行时间的所有好处。

编辑:另请参阅以下@bmb的评论。


@艾登:哈!我的克星,我们再见面!
RichieHindle

56
如果脚本的运行时间不一致,请制作该脚本的四个副本。在开始之前,一个睡眠15秒,另一个,睡30秒,睡45,另一个零。然后每分钟运行全部四个。
bmb

@RichieHindle-不用担心,我因为没有将分钟数细分成几秒钟而被暗杀。但是我在看着你:P
艾登·贝尔

如何每1分钟触发一次cron
DevZer0

实际上,外部脚本应该运行内部脚本三次,而不是四次。否则,最后一分钟的最后一次运行将与下一分钟的第一次运行重叠。那将每分钟运行一次内部脚本5次,而不是4次。
图兰斯·科尔多瓦

290

如果您坚持要从cron运行脚本:

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

并将您的脚本名称和路径替换为/ foo / bar / your_script


18
这是非常聪明的。+1
SingleNegationElimination

4
这对我来说非常有效。上面有关使用后台任务的解决方法是产生了几个子进程并最终导致了内存问题。
Hacknightly,2012年

2
如果运行php脚本,请执行以下操作:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign 2014年

1
如果运行php脚本,则可以将该行#!/usr/bin/php放在php脚本的顶部并使其可执行
Aaron Blenkush 2014年

18
对于必须为此解决方案使用Google感到很尴尬。也许stackoverflow使我少了想。
克里斯·芬尼

15

上面的修改版本:

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

添加到/ 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

不会在后台运行它吗?

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

这几乎是有效的。重要部分仅每15秒执行一次,脚本其余时间进入休眠状态(因此不会浪费周期)。


8
编辑必须至少包含8个字符(这是惯用语,恕我直言),所以我不能&在第3行的末尾添加。在任何情况下,它都不会每15秒运行一次。每“ 15秒+echo hello运行需要花费很长时间”。可能是0.01秒;可能是19个小时。
Parthian开枪



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

自从上一个答案以来,我想出了另一种可能不同甚至更好的解决方案。此代码允许进程每分钟运行60次以上,且精度为微秒。您需要usleep程序才能完成此工作。每秒高达50次应该很好。

#! /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
编辑原始的而不是再次发布!
Vogon Jeltz '16

-1

为避免可能的执行重叠,请使用该线程中所述的锁定机制。


2
-1 OP并未表示他需要避免重复执行;这东西可能是可重入的。另外,这不能回答问题。
Parthian开枪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.