【问题标题】:Repeat a Unix command every x seconds forever永远每隔 x 秒重复一次 Unix 命令
【发布时间】:2010-10-07 23:51:39
【问题描述】:

有一个内置的 Unix 命令 repeat,其第一个参数是重复命令的次数,其中命令(带有任何参数)由 repeat 的其余参数指定。

例如,

% repeat 100 echo "I will not automate this punishment."

将回显给定的字符串 100 次,然后停止。

我想要一个类似的命令——我们称之为forever——除了第一个参数是重复之间暂停的秒数之外,它的工作原理相似,并且它会永远重复。例如,

% forever 5 echo "This will get echoed every 5 seconds forever and ever."

我想我会在写之前先问问这样的东西是否存在。我知道这就像一个 2 行的 Perl 或 Python 脚本,但也许有更标准的方法可以做到这一点。如果没有,请随时使用您最喜欢的脚本语言发布解决方案。

PS:也许更好的方法是将repeat 概括为重复次数(-1 表示无穷大)和重复之间的睡眠秒数。 上面的例子会变成:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."

【问题讨论】:

  • 为什么不:重复 [-t time] [-n number] 命令 [args...]?
  • 酷。不幸的是,在我的 Ubuntu 和 AIX 上 repeat 都是 command not found。你能发一个type repeat 告诉我来自哪里吗?
  • 我也在 Ubuntu 上,没有重复安装。有关如何安装它的任何信息都会有所帮助。
  • repeat 是 csh 和 tcsh 中的内置命令。
  • @KeithThompson、csh、tcsh 和 zsh。虽然它更像是一个关键字而不是内置

标签: command-line scripting


【解决方案1】:

试试watch 命令。

Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] 
             [--no-title] [--version] <command>`

这样:

watch -n1  command

将每秒运行一次命令(好吧,从技术上讲,每一秒加上 commandwatch 运行所需的时间(至少是 procpsbusybox 实现)只睡一秒钟在两次运行 command) 之间,永远。

是否要将命令传递给exec 而不是sh -c,请使用-x 选项:

watch -n1 -x command

在 macOS 上,您可以从 Mac Ports 获取 watch

port install watch

或者你可以从Homebrew获取它:

brew install watch

【讨论】:

  • 你可以使用 MacPorts 来安装它。 sudo 端口安装手表
  • 也可以在自制软件中使用(brew install watch)。
  • +1 用于新的强大工具。我曾经while true; do X; sleep Y; done - 这好多了。
  • 这个工具的问题(至少在 Ubuntu 上)是它强制全屏,所以如果我转储一些周期性信息,我会丢失以前的信息。
  • 它在 FreeBSD 上被称为 cmdwatch(来自端口:sysutils/cmdwatch)。
【解决方案2】:

重击

while + sleep:

while true
do 
    echo "Hi"
    sleep 1
done

这与速记单线相同(来自下面的 cmets):

while sleep 1; do echo "Hi"; done

使用; 分隔命令并使用sleep 1 进行while 测试,因为它始终返回true。您可以在循环中放置更多命令 - 只需将它们用 ; 分隔即可

【讨论】:

  • 我相信它也可以像用通用 Bourne shell 编写一样工作。
  • @dreeves,使用“;”作为分隔符,命令可以在一行中执行。以 Toony 的回答为例
  • sleep 1 始终返回 true,因此您可以将其用作 while 条件。不是世界上最易读的东西,但绝对合法:while sleep 1;做回声“嗨”;完成
  • @David Costa:你的观点很有道理,但sleep 并不总是返回 true。 Ī̲ 使用了这样的循环(尽管延迟时间更长),因为有一种方法可以通过终止睡眠进程来优雅地终止它。
  • @IncnisMrsi 我没有想到,它实际上只在时间过去时返回零,而在被信号终止时返回非零。谢谢指出
【解决方案3】:

这只是其他while+sleep 答案的较短版本,如果您在日常工作中经常运行此类任务,使用它可以避免不必要的按键操作,并且如果您的命令行开始变得更长时间理解这一点一个更容易一些。但是这个是先睡觉开始的。

如果您需要跟踪诸如机器负载之类的单行输出,这通常很有用:

while sleep 1; do uptime; done

【讨论】:

  • 是的。这应该记录在 UUOC 旁边。
  • 如果你想让它像“手表”一样运行,你可以这样做while clear; do date; command;sleep 5; done
  • 哇,感谢while sleep 组合,节省按键!
【解决方案4】:

到目前为止发布的所有答案都有一个问题是执行命令的时间可能会漂移。例如,如果您在命令之间执行sleep 10,并且命令需要 2 秒才能运行,那么它将每 12 秒运行一次;如果运行时间不定,那么从长远来看,运行时间可能无法预测。

这可能正是您想要的;如果是这样,请使用其他解决方案之一,或使用此解决方案但简化 sleep 调用。

对于一分钟的解决方案,cron 作业将在指定的时间运行,而不管每个命令需要多长时间。 (事实上​​,即使前一个任务仍在运行,也会启动一个新的 cron 任务。)

这是一个简单的 Perl 脚本,它会一直休眠到下一个间隔,例如,如果间隔为 10 秒,则命令可能会在 12:34:00、12:34:10、12:34:20 等运行,即使命令本身需要几秒钟。如果命令运行超过interval 秒,将跳过下一个间隔(与 cron 不同)。间隔是相对于纪元计算的,因此 86400 秒(1 天)的间隔将在 UTC 午夜运行。

#!/usr/bin/perl

use strict;
use warnings;

if (scalar @ARGV < 2) {
    die "Usage: $0 seconds command [args...]\n";
}

$| = 1;  # Ensure output appears

my($interval, @command) = @ARGV;

# print ">>> interval=$interval command=(@command)\n";

while (1) {
    print "sleep ", $interval - time % $interval, "\n";
    sleep $interval - time % $interval;
    system @command; # TODO: Handle errors (how?)
}

【讨论】:

  • 在 bash 中更简单,以最大限度地减少漂移(尽管由于 sleep 命令和 bash 本身会累积其微小的执行时间,它可能会在很长的使用窗口内漂移):while :; do sleep 1m &amp; command; wait; done
  • @math:聪明。如果您在当前 shell 中运行其他后台进程,则它不起作用(wait 将等待所有这些进程)。这可以通过将wait 更改为wait $! 来解决,其中$!sleep 进程的PID。将此作为答案发布,我会投票赞成。但它确实会在交互式 shell 中产生一些无关的输出。
【解决方案5】:

我认为到目前为止,这里的所有答案要么过于复杂,要么回答不同的问题:

  • “如何重复运行一个程序,以便在程序完成和下一个开始之间有 X 秒的延迟”

真正的问题是:

  • “如何每 X 秒运行一次程序”

当命令需要时间来完成时,这是两件非常不同的事情。

以脚本foo.sh 为例(假设这是一个需要几秒钟才能完成的程序)。

#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---

您希望每秒运行一次,大多数人会建议 watch -n1 ./foo.shwhile sleep 1; do ./foo.sh; done。但是,这给出了输出:

15:21:20
15:21:23
15:21:27
15:21:30
15:21:34

这并不是每秒都在运行。即使使用-p 标志,正如man watch 页面所建议的那样,可能会解决这个问题,结果是一样的。

完成所需任务的一种简单方法是在后台运行命令。换句话说:

while sleep 1; do (./foo.sh &) ; done

仅此而已。

您可以使用sleep 0.5 或您拥有的每 500 毫秒运行一次。

【讨论】:

  • 投了不同答案的人,不懂你答案的优雅...
  • 这很好,除非您的程序有副作用。如果程序花费的时间比间隔时间长,副作用可能会干扰(如覆盖文件)。如果程序在等待其他东西,而其他东西一直在等待,那么您将无限期地开始越来越多的后台作业。谨慎使用。
  • 可以颠倒后台的顺序以确保一次不超过一个 ./foo.sh 运行,但不超过每秒 1 个:while :; do sleep 1 &amp; ./foo.sh; wait; done
  • 是的,它会漂移,每个循环会漂移几毫秒,但它会。在 foo.sh 中创建 date +"%H:%M:%S.%N" 以查看分数在每个循环中如何缓慢增加。
  • 是的,大多数答案确实没有字面上解决问题。但几乎可以肯定的是,他们回答了 OP 的要求。如果 OP 确实接受了答案,那将是一个更安全的选择......但这很好,如果您知道可能的副作用并且您确定命令的平均运行时间不会超过循环时间
【解决方案6】:

bash:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'

(echo 可以被任何命令替换...

或在perl

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'

print "I will not automate this punishment in absurdum.\n" 可以替换为用反引号 (`) 包围的“任何”命令。

对于暂停,在 for 循环中添加 sleep 语句:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'

【讨论】:

  • while [ 0 ] 是一个不好的写法。这意味着0 是一个长度> 0 的字符串。while [ 1 ]while [ jeff ] 会做同样的事情。最好写while true
  • @Mikel:或while :
【解决方案7】:

最近的 bash >= 4.2 在最近的 Linux 内核下,基于答案。

为了限制执行时间,没有分叉!只使用内置的。

为此,我使用read 内置函数而不是sleep。不幸的是,这不适用于 notty 会话。

按要求快速函数“repeat”:

repeat () {
   local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
   read -t .0001 repeat_foo
   if [ $? = 1 ] ;then
       repeat_sleep() { sleep $1 ;}
   else
       repeat_sleep() { read -t $1 repeat_foo; }
   fi
   shift 2
   while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times))&& ((10#${repeat_delay//.})) &&
            repeat_sleep $repeat_delay
   done
}

带引号字符串的小测试:

repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.

repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C

取决于提交命令的粒度和持续时间...

在最近的 Linux 内核下,有一个 procfile /proc/timer_list 包含以纳秒为单位的时间信息。

如果你想在一秒钟内只运行一次命令,你的命令必须在不到一秒的时间内结束! 并且从那里开始,您必须sleep休息当前秒。

如果延迟更重要并且您的命令不需要大量时间,您可以:

command=(echo 'Hello world.')
delay=10
while :;do
    printf -v now "%(%s)T" -1
    read -t $(( delay-(now%delay) )) foo
    ${command[@]}
  done.

但如果您的目标是获得更精细的粒度,您必须:

使用纳秒信息等到一秒开始...

为此,我写了一个小 bash 函数:

# bash source file for nano wait-until-next-second

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
    local nsnow nsslp
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsecs*}
    nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
    read -t .${nsslp:1} foo
}

采购后,您可以:

command=(echo 'Hello world.')
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

直接在命令行运行${command[@]},而不是比较

command=(eval "echo 'Hello world.';sleep .3")
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

这必须给出完全相同的结果。

按要求聘请函数“repeat”:

你可以参考这个:

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ

repeat_hires () {
    local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
    read -t .0001 repeat_foo
    if [ $? = 1 ] ;then
        repeat_sleep() { sleep $1 ;}
    else
        repeat_sleep() { read -t $1 repeat_foo; }
    fi
    shift 2
    printf -v repeat_delay "%.9f" $repeat_delay
    repeat_delay=${repeat_delay//.}
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsec*}
    started=${nsnow##* }
    while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times)) && ((10#$repeat_delay)) && {
            read -N$TIMER_LIST_READ nsnow </proc/timer_list
            nsnow=${nsnow%% nsec*}
            nsnow=${nsnow##* }
            (( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
                printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
                           "${*}" ${repeat_delay:0:${#repeat_delay}-9
                           }.${repeat_delay:${#repeat_delay}-9}
            printf -v sleep "%010d" $((
                10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
            repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
        }
    done
}

那就试试吧:

time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407

real    0m1.013s
user    0m0.000s
sys     0m0.016s

time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617

real    0m0.257s
user    0m0.000s
sys     0m0.004s

【讨论】:

  • read -t内置,而sleep 不是。这可能会产生边框效果(即点击 [key:return] 悄悄地中断 sleep),但这可以通过测试 $foo 来处理
  • 非常令人印象深刻的答案
【解决方案8】:

如果您不打算在屏幕上显示消息,并且如果您有能力在几分钟内重复这项工作,那么crontab 可能是您最好的工具。例如,如果您希望每分钟执行一次命令,您可以在 crontab 文件中编写如下内容:

* * * * * my_precious_command

请查看教程以获取更多示例。此外,您可以使用Crontab Code Generator 轻松设置时间。

【讨论】:

    【解决方案9】:

    Bash 和它的一些同类 shell 具有方便的 (( ... )) 表示法,其中可以计算算术表达式。

    因此,作为第三个挑战的答案,每次重复之间的重复计数和延迟都应该是可配置的,这是一种方法:

    repeat=10
    delay=1
    
    i=0
    while (( i++ < repeat )); do
      echo Repetition $i
      sleep $delay
    done
    

    这个答案也受到Keith's answer 中提到的时间漂移​​的影响。

    【讨论】:

    • 这是最好的。
    【解决方案10】:

    Perl

    #!/usr/bin/env perl
    # First argument is number of seconds to sleep between repeats, remaining
    # arguments give the command to repeat forever.
    
    $sleep = shift;
    $cmd = join(' ', @ARGV);
    
    while(1) {
      system($cmd);
      sleep($sleep); 
    }
    

    【讨论】:

      【解决方案11】:

      试试这个(Bash):

      forever ()   {
          TIMES=shift;
          SLEEP=shift;
          if [ "$TIMES" = "-1" ]; then
              while true;
              do
                  $@
                  sleep $SLEEP
              done
          else
              repeat "$TIMES" $@
          fi; }
      

      【讨论】:

      • 我对 shell 不感冒,所以欢迎改进。而且我的发行版中似乎没有“重复”命令。
      • repeat 特定于 csh 和 tcsh。
      • @KeithThompson: 和 zsh
      【解决方案12】:

      正如 gbrandt 所说,如果watch 命令可用,请务必使用它。然而,一些 Unix 系统默认没有安装它(至少我工作的地方没有安装)。

      这是另一种语法和输出略有不同的解决方案(适用于 BASH 和 SH):

      while [ 1 ] ; do
          <cmd>
          sleep <x>
          echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
      done
      

      编辑:我删除了一些“。”在最后一个 echo 语句中......我的 Perl 时代的保留;)

      【讨论】:

      • while [ 1 ] 之所以有效,是因为 1 被视为字符串,就像 while [ -n 1 ] 一样。 while [ 0 ]while [ jeff ] 会做同样的事情。 while true 更有意义。
      【解决方案13】:

      如果我们两者都有呢?

      想法是这样的:只有“间隔”,它会永远重复。用“interval”和“times”,重复这个次数,用“interval”隔开。

      用法:

      $ loop [interval [times]] command
      

      所以,算法将是:

      • 列表项
      • 如果 $1 只包含数字,则为区间(默认为 2)
      • 如果 $2 只包含数字,则为次数(默认无限)
      • 带有这些参数的while循环
        • 睡眠“间隔”
        • 如果已经给出了多次,则递减一个 var 直到达到它

      因此:

      loop() {
          local i=2 t=1 cond
      
          [ -z ${1//[0-9]/} ] && i=$1 && shift
          [ -z ${1//[0-9]/} ] && t=$1 && shift && cond=1
          while [ $t -gt 0 ]; do 
              sleep $i
              [ $cond ] && : $[--t]
              $@
          done
      }
      

      【讨论】:

      • 注意:它不适用于浮点数,尽管sleep 确实接受它们。
      【解决方案14】:

      我最终在 swalog 的答案上创建了一个变体。有了他,你必须等待 X 秒才能进行第一次迭代,我也在前台运行我的所以..

      ./foo.sh;while sleep 1; do (./foo.sh) ; done
      

      【讨论】:

        【解决方案15】:

        你可以从 shell 获取 python 解释器的能力。

        python3 -c "import time, os
        while True:
            os.system('your command')
            time.sleep(5)
        

        在您喜欢的任何时间(秒)更换五个。

        注意:返回使用 SHIFT+ENTER 返回 shell 中的输入。并且不要忘记在 while 循环的每一行中输入 4 个 SPACE 缩进。

        【讨论】:

        • 请注意pythonos.system() 会执行一个shell 来解释命令行,因此调用python 在循环中运行shell 以定期运行一个命令有点矫枉过正。你还不如在 shell 中做所有事情。
        • 我同意你的看法。但是,正如您在每个循环中描述的那样,有 5 秒的睡眠时间。因此,可以忽略启动新外壳的额外成本。如果此费用使时间长于预期,您可以根据需要减少一些睡眠时间。
        【解决方案16】:

        快速、肮脏且启动可能很危险,但如果您喜欢冒险并且知道自己在做什么,请将其放入 repeat.shchmod 755 中,

        while true
        do 
            eval $1 
            sleep $2 
        done
        

        ./repeat.sh &lt;command&gt; &lt;interval&gt;调用它

        我的蜘蛛侠感觉这可能是一种邪恶的做法,我的蜘蛛侠感觉对吗?

        【讨论】:

        • 评估!?做什么的? sleep $1; shift; "$@" 或类似的会好得多。
        • 不知道为什么这是-2。 eval 可能是矫枉过正......除非你需要它。 Eval 可以让你得到诸如重定向到命令行之类的东西。
        【解决方案17】:

        您可以从 init 运行脚本(在 /etc/inittab 中添加一行)。该脚本必须运行您的命令,休眠一段时间,直到再次运行该脚本,然后退出。 Init 将在退出后再次启动您的脚本。

        【讨论】:

          【解决方案18】:

          从 crontab 以小于一分钟的间隔重复作业的简单方法(例如 20 秒):

          crontab: * * * * * script.sh

          script.sh:

          #!/bin/bash
          >>type your commands here.
          
          sleep 20
          >>retype your commands here.
          
          sleep 20
          >>retype your commands here.
          

          【讨论】:

            【解决方案19】:

            你可以获取这个递归函数:

            #!/bin/bash
            ininterval () {
                delay=$1
                shift
                $*
                sleep $delay
                ininterval $delay $*
            }
            

            或添加:

            ininterval $*
            

            并调用脚本。

            【讨论】:

              【解决方案20】:

              要在控制台窗口中重复运行命令,我通常会运行如下内容:

              while true; do (run command here); done

              这也适用于多个命令,例如,在控制台窗口中显示不断更新的时钟:

              while true; do clear; date; sleep 1; done

              【讨论】:

              • 为什么投反对票?通过将示例复制并粘贴到终端窗口中,您可以看到这是一个有效的解决方案。
              • 我认为你被否决了,因为它有点危险并且会消耗 CPU。
              【解决方案21】:
              #! /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
              

              【讨论】:

                【解决方案22】:

                使用zsh 和接受浮点参数的sleep 实现:

                typeset -F SECONDS=0 n=0
                repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}
                

                或者forever

                for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}
                

                如果您的 sleep 不支持浮点数,您可以随时将其重新定义为 zshzselect 内置的包装器:

                zmodload zsh/zselect
                sleep() zselect -t $((($1 * 100) | 0))
                

                【讨论】:

                  【解决方案23】:

                  此解决方案适用于 Mac OS X v10.7 (Lion)。效果很好。

                  bash -c 'while [ 0 ]; do \
                        echo "I will not automate this punishment in absurdum."; done'
                  

                  就我而言

                  bash -c 'while [ 0 ]; do ls; done'
                  

                  bash -c 'while [ 0 ]; do mv "Desktop/* Documents/Cleanup"; done'
                  

                  不断清理我的桌面。

                  【讨论】:

                  • 你的意思是在那里有一个sleep 命令吗?
                  • while [ 0 ] 是一种写无限循环的奇怪方式;它之所以有效,是因为test 命令(也称为[)将非空字符串视为真。 while : ; do ... ; done 更惯用,或者如果您愿意,可以使用 while true ; do ... ; done
                  【解决方案24】:

                  ...我想知道解决这个问题时可以创建多少复杂的解决方案。

                  原来如此简单……

                  open /etc/crontab 
                  

                  将下一行放在文件末尾,例如:

                  */NumberOfSeconds * * * * user /path/to/file.sh
                  

                  如果你想每 1 秒运行一次,就放在那里:

                  */60 * * * * root /path/to/file.sh 
                  
                  where that file.sh could be chmod 750 /path/to/file.sh
                  

                  该 file.sh 的内部应该是:

                  #!/bin/bash 
                  #What does it do
                  #What is it runned by
                  
                  your code or commands
                  

                  仅此而已!

                  尽情享受吧!

                  【讨论】:

                  • 这是不正确的。 */60 将每 60 分钟运行一次,例如,*/5 每 5 分钟运行一次。
                  • crontab 上不允许秒数
                  • 在回答错误之前测试你的东西。
                  【解决方案25】:
                  until ! sleep 60; do echo $(date); command; command; command; done
                  

                  为我工作。

                  1. 我不需要每 60 秒一次
                  2. “watch”不会在所有系统上监视命令
                  3. “watch”只能接受一个命令(或脚本文件)
                  4. 将睡眠置于条件而不是主体中可以使循环更好地中断(因此,在 stackoverflow 上对类似问题的回复中声明。不幸的是,我现在可以找到它)
                  5. 我想在延迟之前执行一次,所以我使用 until 而不是 while

                  【讨论】:

                  • 我刚刚注意到“直到”显然也在第一次迭代之前执行了睡眠。有什么方法可以将条件放在 bash 中的循环末尾?
                  猜你喜欢
                  • 1970-01-01
                  • 2021-04-22
                  • 1970-01-01
                  • 2015-10-12
                  • 2020-02-02
                  • 1970-01-01
                  • 2014-12-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多