希望
パイプラインに応答してコマンドを繰り返し実行したいと思います。
firehose | expensive-command
しかし、私は多くの行を受け取り、コマンドは非常にリソース集約的です。最大1回だけ実行するようにコマンドへの入力をフィルタリングしたいと思います。X第二:
firehose | interval 1 second | expensive-command
このinterval
コマンドは単純なフィルタにしてはいけません。むしろ、クールダウン期間中に到着したすべてのアイテムをブロックするのではなく、クールダウン期間の終わりに最新の受信行を送信する必要があります。
どうすればいいですか?
試みる
epoch () { date +%s --date="$*" }
interval () {
INTERVAL="$*"
LAST_RUN_AT=0
WHEN_TO_RUN=0
while read LINE; do
if (( $(epoch now) >= $WHEN_TO_RUN )) then
echo $LINE
WHEN_TO_RUN="$(epoch now + $INTERVAL)"
fi
done
}
alias firehose='(print "1\n2\n3" ; sleep 2 ; print "4\n")'
alias expensive-command='cat'
firehose | interval 1 second | expensive-command
これはほとんど機能しますが、出荷ラインを後で遅らせることができないという問題があります。すぐに出荷するかキャンセルするかを決定できます。
何が起こったのか:
1
4
スロットルはそれを受け取り、それを渡して1
冷却し続けます。冷却期間1
中に到着するので3
完全に廃棄されます。クールタイムが4
到達する前に終了して配信されました。
私がしたいこと:
1
3
4
受信後、1
スロットルは1秒間冷却する必要があります。その後、2
まだ冷却しているので、後で受け取って保管する必要があります。その後、それを受け取り、後で送信された内容を3
置き換えます。2
その後、スロットルは冷却を停止し、その時点ですぐに送信する必要があります3
。最後に、4
ターンのクールダウンが完了すると到着するので、すぐに送信されます。
zshにいる場合閉鎖、休止状態のサブシェルを起動し、ついに$INTERVAL
これecho
を受け取りますLINE
が、残念ながらzshにはクロージャはありません。
ベストアンサー1
問題は読み取りには時間制限が必要です。。何も送信しないとfirehose
ループ無期限のブロックそして、これを行うと、最後に受信した行の送信に失敗します。Bashには、タイムアウト読み取りを表す-tパラメーターがあります。zshにread
これがあれば使用できます。
アルゴリズムは、常に行を読み取り、1秒(または他の)間隔の終わりに期限切れになるように常に再計算される(ますます短くなる)タイムアウトを設定することです。この間隔に達すると、1 つ以上の行を読み取ると、最後の行が送信されます。それ以外の場合は何も送信されず、次の行間隔を読み始めます。
受信した最初のラインまたはインターバル時間より長い後、受信した最初のラインに「即時配信」を実装できます。間隔が1秒で、最後のラインが出力されてfirehose
から1.5秒間何もないと、ラインが通過する可能性があり、マシンはその時点で新しい1秒間隔を開始するようにリセットできます。
TXR Lispのこの概念実証実装は私にとってうまく機能し、基本的なアルゴリズムを検証します。
(defvarl %interval% 1000000) ;; us
(defun epoch-usec ()
(tree-bind (sec . usec) (time-usec)
(+ (* 1000000 sec) usec)))
(let ((now (epoch-usec))
(*stdin* (open-fileno (fileno *stdin*) "rl")) ;; line buffered
remaining-time next-time line done)
(while (not done)
(set next-time (+ now %interval%))
(set remaining-time (- next-time now))
(while (poll (list (cons *stdin* poll-in))
(trunc remaining-time 1000))
;; got a line or EOF poll: no timeout
(iflet ((nline (get-line)))
(set line nline) ;; got line
(progn (flip done) (return))) ;; EOF poll
(set now (epoch-usec))
(when (minusp (set remaining-time (- next-time now)))
(return)))
;; timeout, past deadline or exit: flush line, if any:
(when line
(put-line line)
(set line nil))))
poll
タイムアウト読み取りが使用されており、poll
ストリームバッファが表示されないため、バッファリングされていないストリームを設定してください。ストリームに読み込まれていないバッファリングされたデータがあるときに入力をポーリングしたくないというアイデアです。これはnitpickです。テストでは、*stdin*
この動作とバッファリングされたネイティブストリームの使用との間の動作の質的な違いは実際には見られませんでした。ストリームにバッファリングされたデータがあるがファイル記述子にデータがない場合にポーリング時間を浪費する場合、間隔より長く待たずに新しいデータが早く到着すると、待ち時間は間隔より短くなります。
我々は成功がpoll
全行を読むことができることを意味すると仮定する。poll
もちろん、これを保証することはできませんが、正しく機能するテキストストリーム入力ソースは、入力バイトがウェイクアップに使用可能な場合、そのpoll
バイトの後に過度の遅延なしに完全な行が続くことを保証する必要があります。
残り時間の計算には、カレンダーの時間とpoll
時間の調整に敏感ではない可能性がある相対的な待機のみが使用されます。したがって、一般的な注意事項が適用されます。時計が突然後ろに戻ったら、こんな!
これらのテストケースは、顕著な遅延なしに行われます。
$ echo foo | txr throttle.txr
foo
$ (echo foo; echo bar) | txr throttle.tl
bar
$ (echo foo; echo bar; echo xyzzy) | txr throttle.tl
xyzzy
それから:
$ (echo foo; sleep 2; echo bar; sleep 2; echo xyzzy) | txr throttle.tl
foo
bar
xyzzy
find / | txr throttle.tl
などをテストしてみました。