組み込みの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."
watch
コマンドを試してください。
Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>]
[--no-title] [--version] <command>`
そのため:
watch -n1 command
コマンドは毎秒実行されます(技術的には、1秒ごとにcommand
がwatch
として実行されるのにかかる時間(少なくともprocps
およびbusybox
実装)は、2回のcommand
)の実行の間、1秒間だけ永遠にスリープします。
コマンドをsh -c
ではなくexec
に渡したい場合は、-x
オプションを使用します。
watch -n1 -x command
MacOSでは、watch
は Mac Ports から取得できます。
port install watch
または Homebrew から取得できます。
brew install watch
バッシュ
while
+ sleep
:
while true
do
echo "Hi"
sleep 1
done
以下は、短縮形のワンライナーと同じものです(以下のコメントから):
while sleep 1; do echo "Hi"; done
;
を使用してコマンドを区切り、常に[true]を返すため、while
テストにはsleep 1
を使用します。より多くのコマンドをループに入れることができます-;
でそれらを分離するだけです
これは、他のwhile+sleep
回答の短いバージョンにすぎません。この種のタスクを日常業務として頻繁に実行している場合、これを使用すると、不要なキーを押す必要がなくなり、コマンドラインでこれを理解しやすくなります。少し簡単です。しかし、これは最初に眠ることから始まります。
これは通常、マシンの負荷のような1行の出力があるものを追跡する必要がある場合に役立ちます。
while sleep 1; do uptime; done
これまでに投稿されたすべての回答の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?)
}
ここまでの答えはすべて複雑すぎるか、別の質問に答えていると思います。
本当の質問は:
コマンドの完了に時間がかかる場合、これらは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
、またはあなたは何をしています。
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}'
実行時間を制限するために、フォークがありません!組み込みのみが使用されます。
これには、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
ナノ秒単位の時間情報を含みます。
コマンドを正確に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.
ただし、より細かい粒度を得ることが目標である場合は、次のことを行う必要があります。
このために、私は少し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
Bashとその同類のシェルには、便利な(( ... ))
表記。算術式を評価できます。
したがって、繰り返し回数と各繰り返し間の遅延の両方を構成可能にする必要がある3番目の課題への回答として、これを行う1つの方法を次に示します。
repeat=10
delay=1
i=0
while (( i++ < repeat )); do
echo Repetition $i
sleep $delay
done
Keith'sanswer で説明されているタイミングドリフトもこの回答の影響を受けます。
画面にメッセージを表示するつもりがなく、分単位でジョブを繰り返す余裕がある場合は、おそらく crontab が最適なツールです。たとえば、毎分コマンドを実行したい場合は、crontab
ファイルに次のように記述します。
* * * * * my_precious_command
さらなる例については、チュートリアルをチェックしてください。また、 Crontab Code Generator を使用して、タイミングを簡単に設定できます。
#!/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);
}
これを試してみませんか(Bash)?
forever () {
TIMES=shift;
SLEEP=shift;
if [ "$TIMES" = "-1" ]; then
while true;
do
$@
sleep $SLEEP
done
else
repeat "$TIMES" $@
fi; }
Gbrandtで言及されているように、watch
コマンドが使用可能な場合は、それを使用してください。ただし、一部のUnixシステムでは、デフォルトでインストールされていません(少なくとも、私が作業している場所ではインストールされていません)。
構文と出力が少し異なる別のソリューションを次に示します(BASHとSHで機能します)。
while [ 1 ] ; do
<cmd>
sleep <x>
echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
done
編集:「。」をいくつか削除しました最後のエコーステートメントで...私のPerl時代からのホールドオーバー;)
両方あったらどうなるの?
これがアイデアです。「間隔」だけで、それは永遠に繰り返されます。 「間隔」と「時間」を使用すると、「間隔」で区切られたこの回数が繰り返されます。
使い方:
$ loop [interval [times]] command
したがって、アルゴリズムは次のようになります。
したがって:
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
}
私はswalogの答えにバリアントを作成してしまいました。彼の場合、最初の反復でX秒待機する必要があり、私もフォアグラウンドで実行しています。
./foo.sh;while sleep 1; do (./foo.sh) ; done
シェルからpythonインタプリタからパワーを得ることができます。
python3 -c "import time, os
while True:
os.system('your command')
time.sleep(5)
あなたが好きな任意の時間(秒)で5を交換してください。
注意:リターンでは、SHIFT + ENTERを使用して、シェルで入力を返します。また、whileループの各行に4 [〜#〜] space [〜#〜]インデントを入力することを忘れないでください。
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.
Initからスクリプトを実行できます(/ etc/inittabに行を追加します)。このスクリプトは、コマンドを実行し、スクリプトが再度実行されるまで待機する時間だけスリープして、終了する必要があります。 Initは終了後にスクリプトを再び開始します。
速くて汚い、おそらく起動するのは危険ですが、冒険好きで何をしているのかわかっている場合は、これをrepeat.sh
とchmod 755
に入れてください。
while true
do
eval $1
sleep $2
done
./repeat.sh <command> <interval>
で呼び出します
私のスパイダーセンスは、これはおそらくこれを行うには悪い方法だと言っています。私のスパイダーセンスは正しいのでしょうか。
#! /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
...この問題を解決するときに、どれほど複雑なソリューションを作成できるのでしょうか。
それはとても簡単です...
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
そしてそれだけです!
楽しい!
zsh
と、浮動小数点引数を受け入れるsleep
実装の場合:
typeset -F SECONDS=0 n=0
repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}
またはforever
の場合:
for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}
sleep
がfloatをサポートしていない場合は、zsh
のzselect
組み込み関数のラッパーとしていつでも再定義できます。
zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))
この再帰関数をソースにすることができます:
#!/bin/bash
ininterval () {
delay=$1
shift
$*
sleep $delay
ininterval $delay $*
}
または追加:
ininterval $*
スクリプトを呼び出します。
コンソールウィンドウでコマンドを繰り返し実行するには、通常次のように実行します。
while true; do (run command here); done
これは複数のコマンドでも機能します。たとえば、継続的に更新される時計をコンソールウィンドウに表示します。
while true; do clear; date; sleep 1; done
このソリューションは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'
デスクトップを常にクリーンアップします。