web-dev-qa-db-ja.com

Unixコマンドをx秒ごとに永久に繰り返します

組み込みの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."
497
dreeves

watchコマンドを試してください。

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

そのため:

watch -n1  command

コマンドは毎秒実行されます(技術的には、1秒ごとにcommandwatchとして実行されるのにかかる時間(少なくともprocpsおよびbusybox実装)は、2回のcommand)の実行の間、1秒間だけ永遠にスリープします。

コマンドをsh -cではなくexecに渡したい場合は、-xオプションを使用します。

watch -n1 -x command

MacOSでは、watchMac Ports から取得できます。

port install watch

または Homebrew から取得できます。

brew install watch
637
Gregor Brandt

バッシュ

while + sleep

while true
do 
    echo "Hi"
    sleep 1
done

以下は、短縮形のワンライナーと同じものです(以下のコメントから):

while sleep 1; do echo "Hi"; done

;を使用してコマンドを区切り、常に[true]を返すため、whileテストにはsleep 1を使用します。より多くのコマンドをループに入れることができます-;でそれらを分離するだけです

255
pope

これは、他のwhile+sleep回答の短いバージョンにすぎません。この種のタスクを日常業務として頻繁に実行している場合、これを使用すると、不要なキーを押す必要がなくなり、コマンドラインでこれを理解しやすくなります。少し簡単です。しかし、これは最初に眠ることから始まります。

これは通常、マシンの負荷のような1行の出力があるものを追跡する必要がある場合に役立ちます。

while sleep 1; do uptime; done
73
Bekir Dogan

これまでに投稿されたすべての回答の1つの問題は、コマンドの実行時間がずれる可能性があることです。たとえば、sleep 10コマンド間で、コマンドの実行には2秒かかり、その後12秒ごとに実行されます。実行にさまざまな時間がかかる場合、長期的には実行時間が予測不能になる可能性があります。

これはまさにあなたが望むものかもしれません。その場合は、他のソリューションの1つを使用するか、これを使用してsleep呼び出しを簡略化します。

1分の解決では、各コマンドの所要時間に関係なく、cronジョブは指定された時間に実行されます。 (実際には、前のジョブがまだ実行されている場合でも、新しいcronジョブが起動します。)

次の間隔までスリープする単純なPerlスクリプトを次に示します。たとえば、10秒の間隔でコマンドを実行しても、12:34:00、12:34:10、12:34:20などの場合があります。コマンド自体は数秒かかります。コマンドの実行時間がinterval秒を超えると、次の間隔がスキップされます(cronとは異なります)。間隔はエポックを基準に計算されるため、UTCの午前0時に86400秒(1日)の間隔が実行されます。

#!/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?)
}
46
Keith Thompson

ここまでの答えはすべて複雑すぎるか、別の質問に答えていると思います。

  • "プログラムを繰り返し実行して、プログラムが終了してから次の開始までにX秒の遅延を設ける方法"

本当の質問は:

  • "X秒ごとにプログラムを実行する方法"

コマンドの完了に時間がかかる場合、これらは2つの非常に異なるものです。

たとえば、スクリプトfoo.sh(これが完了するまでに数秒かかるプログラムであると仮定します)。

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

あなたは毎秒これを実行したいと思っており、ほとんどはwatch -n1 ./foo.sh、またはwhile 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

それがすべてです。

500ミリ秒ごとにsleep 0.5、またはあなたは何をしています。

33
swalog

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"は、バッククォート( `)で囲まれた「any」コマンドに置き換えることができます。

一時停止するには、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}'
17
Tooony

最近のLinuxカーネルでの最近のbash> = 4.2、ベースの回答。

実行時間を制限するために、フォークがありません!組み込みのみが使用されます。

これには、readの代わりにsleep組み込み関数を使用します。残念ながら、これはnottyセッションでは機能しません。

クイック bash 要求に応じて「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ナノ秒単位の時間情報を含みます。

コマンドを正確に1秒ごとに実行する場合、コマンドは1秒未満で終了する必要があります。 そしてそこから、sleepのみ現在の秒の残り

遅延がより重要で、コマンドに大きな時間を必要としない場合は、次のことができます。

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

ただし、より細かい粒度を得ることが目標である場合は、次のことを行う必要があります。

ナノ秒の情報を使用して、1秒の始まりまで待機します...

このために、私は少し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.

これは正確に同じ結果を与える必要があります。

要求に応じて採用 bash 関数 "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
10
F. Hauri

Bashとその同類のシェルには、便利な(( ... ))表記。算術式を評価できます。

したがって、繰り返し回数と各繰り返し間の遅延の両方を構成可能にする必要がある3番目の課題への回答として、これを行う1つの方法を次に示します。

repeat=10
delay=1

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

Keith'sanswer で説明されているタイミングドリフトもこの回答の影響を受けます。

4
Thor

画面にメッセージを表示するつもりがなく、分単位でジョブを繰り返す余裕がある場合は、おそらく crontab が最適なツールです。たとえば、毎分コマンドを実行したい場合は、crontabファイルに次のように記述します。

* * * * * my_precious_command

さらなる例については、チュートリアルをチェックしてください。また、 Crontab Code Generator を使用して、タイミングを簡単に設定できます。

4
Barun

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); 
}
4
dreeves

これを試してみませんか(Bash)?

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

Gbrandtで言及されているように、watchコマンドが使用可能な場合は、それを使用してください。ただし、一部のUnixシステムでは、デフォルトでインストールされていません(少なくとも、私が作業している場所ではインストールされていません)。

構文と出力が少し異なる別のソリューションを次に示します(BASHとSHで機能します)。

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

編集:「。」をいくつか削除しました最後のエコーステートメントで...私のPerl時代からのホールドオーバー;)

3
bedwyr

両方あったらどうなるの?

これがアイデアです。「間隔」だけで、それは永遠に繰り返されます。 「間隔」と「時間」を使用すると、「間隔」で区切られたこの回数が繰り返されます。

使い方:

$ loop [interval [times]] command

したがって、アルゴリズムは次のようになります。

  • リストアイテム
  • $ 1に数字のみが含まれる場合、それは間隔です(デフォルトは2)
  • $ 2に数字のみが含まれる場合、それは回数です(デフォルトは無限)
  • これらのパラメータを使用してループする
    • スリープ「インターバル」
    • 回数が指定されている場合は、到達するまで変数をデクリメントします

したがって:

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
}
3
Baronsed

私はswalogの答えにバリアントを作成してしまいました。彼の場合、最初の反復でX秒待機する必要があり、私もフォアグラウンドで実行しています。

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

シェルからpythonインタプリタからパワーを得ることができます。

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

あなたが好きな任意の時間(秒)で5を交換してください。

注意:リターンでは、SHIFT + ENTERを使用して、シェルで入力を返します。また、whileループの各行に4 [〜#〜] space [〜#〜]インデントを入力することを忘れないでください。

1
pah8J

Crontabから1分未満の間隔(20秒の例)でジョブを繰り返す簡単な方法:

crontab:* * * * * script.sh

script.sh:

#!/bin/bash
>>type your commands here.

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.
1
Charles nakhel

Initからスクリプトを実行できます(/ etc/inittabに行を追加します)。このスクリプトは、コマンドを実行し、スクリプトが再度実行されるまで待機する時間だけスリープして、終了する必要があります。 Initは終了後にスクリプトを再び開始します。

1
Roberto Paz

速くて汚い、おそらく起動するのは危険ですが、冒険好きで何をしているのかわかっている場合は、これをrepeat.shchmod 755に入れてください。

while true
do 
    eval $1 
    sleep $2 
done

./repeat.sh <command> <interval>で呼び出します

私のスパイダーセンスは、これはおそらくこれを行うには悪い方法だと言っています。私のスパイダーセンスは正しいのでしょうか。

1
mallyone
#! /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
user56318

...この問題を解決するときに、どれほど複雑なソリューションを作成できるのでしょうか。

それはとても簡単です...

open /etc/crontab 

次のように、ファイルの最後に次の1行を挿入します。

*/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

そしてそれだけです!

楽しい!

0
MIrra

zshと、浮動小数点引数を受け入れるsleep実装の場合:

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

またはforeverの場合:

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

sleepがfloatをサポートしていない場合は、zshzselect組み込み関数のラッパーとしていつでも再定義できます。

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

この再帰関数をソースにすることができます:

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

または追加:

ininterval $*

スクリプトを呼び出します。

0
user unknown

コンソールウィンドウでコマンドを繰り返し実行するには、通常次のように実行します。

while true; do (run command here); done

これは複数のコマンドでも機能します。たとえば、継続的に更新される時計をコンソールウィンドウに表示します。

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

0
Thomas Bratt

このソリューションはMacOSX 10.7で機能します。それは素晴らしい働きをします。

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'

デスクトップを常にクリーンアップします。

0
Dan Ruiz