จะทำให้สคริปต์ Unix ทำงานทุก ๆ 15 วินาทีได้อย่างไร


84

ฉันเคยเห็นวิธีแก้ปัญหาบางอย่างรวมถึงนาฬิกาและเพียงแค่เรียกใช้สคริปต์วนซ้ำ (และนอนหลับ) ในพื้นหลัง แต่ไม่มีอะไรที่เหมาะ

ฉันมีสคริปต์ที่ต้องทำงานทุก ๆ 15 วินาทีและเนื่องจาก cron ไม่รองรับวินาทีฉันจึงเหลือที่จะหาอย่างอื่น

วิธีใดที่มีประสิทธิภาพและมีประสิทธิภาพที่สุดในการรันสคริปต์ทุก ๆ 15 วินาทีบนยูนิกซ์ สคริปต์ยังต้องทำงานหลังจากรีบูต


1
วิ่งนานแค่ไหน?
Adam Matan

คำตอบ:


77

ฉันจะใช้ cron เพื่อรันสคริปต์ทุกๆนาทีและทำให้สคริปต์นั้นรันสคริปต์ของคุณสี่ครั้งด้วยการพัก 15 วินาทีระหว่างการรัน

(ซึ่งถือว่าสคริปต์ของคุณทำงานได้อย่างรวดเร็ว - คุณสามารถปรับเวลาการนอนหลับได้หากไม่เป็นเช่นนั้น)

ด้วยวิธีนี้คุณจะได้รับประโยชน์ทั้งหมดcronเช่นเดียวกับระยะเวลาการวิ่ง 15 วินาทีของคุณ

แก้ไข:ดูความคิดเห็นของ @ bmb ด้านล่าง


@ ไอเดน: ฮะ! ความซวยของฉันเจอกันอีกแล้ว!
RichieHindle

56
หากสคริปต์ไม่สอดคล้องกับระยะเวลาในการรันให้ทำสำเนาสคริปต์สี่ชุด คนหนึ่งหลับ 15 วินาทีก่อนเริ่มอีก 30 วินาทีอีก 45 วินาทีและอีกศูนย์ จากนั้นเรียกใช้ทั้งสี่ทุกนาที
bmb

@RichieHindle - ไม่ต้องกลัวฉันถูกลอบสังหารเพราะไม่ย่อยนาทีเป็นวินาที แต่ฉันกำลังดูคุณ: P
Aiden Bell

cron จะถูกกระตุ้นทุก ๆ 1 นาทีได้อย่างไร
DevZer0

จริงๆแล้วสคริปต์นอกควรเรียกใช้สคริปต์ภายในสามครั้งไม่ใช่สี่ครั้ง มิฉะนั้นการวิ่งสุดท้ายของนาทีสุดท้ายจะทับซ้อนกับการวิ่งครั้งสุดท้ายของนาทีถัดไป นั่นจะเป็นการเรียกใช้สคริปต์ภายใน 5 ครั้งทุก ๆ นาทีแทนที่จะเป็นสี่ครั้ง
Tulains Córdova

292

หากคุณยืนยันที่จะเรียกใช้สคริปต์ของคุณจาก 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


4
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน การแก้ปัญหาข้างต้นเกี่ยวกับการใช้งานพื้นหลังทำให้เกิดกระบวนการย่อยหลายอย่างและทำให้เกิดปัญหาหน่วยความจำในตอนท้ายของฉัน
Hacknightly

3
หากเรียกใช้สคริปต์ php ให้ทำสิ่งนี้:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign

1
หากเรียกใช้สคริปต์ php คุณสามารถนำหน้าบรรทัด#!/usr/bin/phpที่ด้านบนสุดของสคริปต์ php ของคุณและทำให้สามารถเรียกใช้งานได้
Aaron Blenkush

18
ฉันรู้สึกอายที่ต้องใช้ Google สำหรับวิธีแก้ปัญหานี้ บางที stackoverflow ทำให้ฉันคิดน้อยลง
chris finne

ในการตอบสนองต่อ @Hacknightly จะเกิดขึ้นเฉพาะเมื่อสคริปต์เกินรันไทม์ 15 วินาทีและ / หรืองานไม่สามารถปล่อยหน่วยความจำเพียงพอที่ระบบจะใช้งานได้
Abel Callejo

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 ตัวอักษร (ซึ่งเป็นเรื่องงี่เง่า IMHO) ดังนั้นฉันจึงไม่สามารถเพิ่ม&ท้ายบรรทัด 3 ได้ไม่ว่าในกรณีใดก็ตามการดำเนินการนี้จะไม่ทำงานทุกๆ 15 วินาที การดำเนินการนี้จะทำงานทุกๆ "15 วินาทีecho helloขึ้นไป ซึ่งอาจเป็น 0.01 วินาที อาจเป็น 19 ชั่วโมง
Parthian Shot

1

ฉันเขียนกำหนดการเร็วกว่า cron ฉันยังใช้ยามซ้อนทับ คุณสามารถกำหนดค่าตัวกำหนดตารางเวลาเพื่อไม่ให้เริ่มกระบวนการใหม่หากก่อนหน้านี้ยังคงทำงานอยู่ ดูที่https://github.com/sioux1977/scheduler/wiki


0

ใช้นาโนสลีป (2) . ใช้โครงสร้างtimespecที่ใช้ระบุช่วงเวลาด้วยความแม่นยำระดับนาโนวินาที

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

1
ฉันจะไปข้างหน้าและเดาว่าพวกเขาไม่ต้องการความแม่นยำระดับนาโนวินาทีเนื่องจากสิ่งที่พวกมันวางไข่ทุก ๆ 15 วินาทีคือเชลล์สคริปต์ไม่ใช่เคอร์เนลเธรด
Parthian Shot

@ParthianShot อาจจะ แต่คุณไม่เคยรู้
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

เนื่องจากคำตอบก่อนหน้าของฉันฉันได้หาวิธีแก้ปัญหาอื่นที่แตกต่างและอาจจะดีกว่า รหัสนี้อนุญาตให้เรียกใช้กระบวนการมากกว่า 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

-1

เพื่อหลีกเลี่ยงการดำเนินการที่ทับซ้อนกันให้ใช้กลไกการล็อกตามที่อธิบายไว้ในเธรดนั้น


2
-1 OP ไม่ได้บอกว่าเขาจำเป็นต้องหลีกเลี่ยงการดำเนินการที่ทับซ้อนกัน สิ่งนั้นสามารถกลับเข้ามาใหม่ได้ แถมนี่ยังไม่ตอบโจทย์
Parthian Shot
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.