毎秒何かを確認して印刷するためにこのループを実行しています。ただし、計算に数百ミリ秒かかることがあるため、印刷時間が1秒をスキップすることがあります。
毎秒印刷物を取得できるようにするこのようなループを作成する方法はありますか? (もちろん、ループの計算時間が1秒未満の場合:))
while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' $TIME $FOO $BAR
sleep 1
done
答え1
元のコードに近づくために私がしたことは次のとおりです。
while true; do
sleep 1 &
...your stuff here...
wait # for sleep
done
これは意味を少し変更します。タスクに1秒未満がかかる場合は、1秒が経過するのを待ちます。ただし、何らかの理由でジョブが1秒以上かかる場合、より多くのサブプロセスは生成されず、絶対に終了しません。
したがって、ジョブは並列に実行されるか、バックグラウンドで実行されないため、変数は期待どおりに機能します。
他のバックグラウンドタスクも開始する場合は、そのプロセスのみを具体的wait
に待つように指示を変更する必要があります。sleep
より正確にする必要がある場合は、システムクロックに同期して、フル秒ではなくミリ秒間スリープモードに切り替えることができます。
システムクロックと同期するには?本当に分からない、愚かな試み:
基本:
while sleep 1
do
date +%N
done
出力: 003511461 010510925 016081282 021643477 028504349 03... (継続的に増加)
同期済み:
while sleep 0.$((1999999999 - 1$(date +%N)))
do
date +%N
done
出力:002648691 001098397 002514348 001293023 001679137 00...(未変更のまま)
答え2
ループをスクリプト/onelinerに再構成できる場合は、最も簡単な方法は次を使用することです。watch
そしてそのprecise
オプション。
次のコマンドを使用して効果を確認できますwatch -n 1 sleep 0.5
。秒数を表示しますが、時々1秒をスキップすることもあります。これを実行すると、watch -n 1 -p sleep 0.5
毎秒2回出力され、スキップする現象は表示されません。
答え3
バックグラウンドジョブとして実行されているサブシェルでジョブを実行すると、そのジョブがsleep
.
while true; do
(
TIME=$(date +%T)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' "$TIME" "$FOO" "$BAR"
) &
sleep 1
done
1秒で「盗んだ」唯一の時間はサブシェルの実行に費やされた時間なので、最終的に1秒はスキップされますが、元のコードより短くなることを願っています。
サブシェルのコードが次を使用する場合もっと1秒未満では、ループはバックグラウンドタスクを蓄積し始め、最終的にリソースが不足します。
答え4
そしてzsh
:
n=0
typeset -F SECONDS=0
while true; do
date '+%FT%T.%2N%z'
((++n > SECONDS)) && sleep $((n - SECONDS))
done
zsh
Sleepが浮動小数点秒をサポートしていない場合は、代わりに 'を使用できますzselect
(後でzmodload zsh/zselect
)。
zmodload zsh/zselect
n=0
typeset -F SECONDS=0
while true; do
date '+%FZ%T.%2N%z'
((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
done
ループ内の命令が1秒未満実行される限り、ドリフトしないでください。