私は2つの別々のコマンドで構成されたコマンドが欲しいですtime
。 1 つは出力を別のコマンドにパイプします。たとえば、次の2つのスクリプトを考えてみましょう。
$ cat foo.sh
#!/bin/sh
sleep 4
$ cat bar.sh
#!/bin/sh
sleep 2
今のtime
時間をどのように報告できますかfoo.sh | bar.sh
? (はい、ここではパイプが意味をなさないことがわかりますが、これは単なる例です。)パイプなしでサブシェルで順番に実行すると、期待どおりに機能します。
$ time ( foo.sh; bar.sh )
real 0m6.020s
user 0m0.010s
sys 0m0.003s
ただし、パイピング時に機能させることはできません。
$ time ( foo.sh | bar.sh )
real 0m4.009s
user 0m0.007s
sys 0m0.003s
$ time ( { foo.sh | bar.sh; } )
real 0m4.008s
user 0m0.007s
sys 0m0.000s
$ time sh -c "foo.sh | bar.sh "
real 0m4.006s
user 0m0.000s
sys 0m0.000s
同様の質問を読んだ(複数のコマンドで時間を実行し、時間出力をファイルに書き込む方法は?)スタンドアロンtime
実行ファイルを試しました。
$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00
パイプラインのみを実行する3番目のスクリプトを生成しても機能しません。
$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh
それから時間:
$ time baz.sh
real 0m4.009s
user 0m0.003s
sys 0m0.000s
time
興味深いことに、最初のコマンドが完了した直後に終了しないようです。次のように変更した場合bar.sh
:
#!/bin/sh
sleep 2
seq 1 5
繰り返しますが、以前に出力が印刷されるとtime
予想していましたが、そうではありません。time
seq
$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5
real 0m4.005s
user 0m0.003s
sys 0m0.000s
レポート1を印刷する前に完了するのを待っているにもかかわらず、time
実行にかかる時間は計算されないようです。bar.sh
すべてのテストは、Archシステムでbash 4.4.12(1)リリースを使用して実行されました。私はプロジェクトでbashだけを使うことができ、これはそれの一部なので、zsh
他の強力なシェルがこの問題を解決できるとしても、それは私にとって実行可能な解決策ではありません。
それでは、一連のパイプラインコマンドを実行するのにかかる時間をどのように取得できますか?そして、私たちがこれを行うとき、なぜ動作しませんか?time
最初のコマンドが完了するとすぐに終了するようです。なぜ?
私は次の方法で個人的な時間を持つことができることを知っています。
( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time
しかし、すべてのタスクを単一のタスクで時間を測定することが可能であるかどうかはまだ知りたいです。
1 これはバッファの問題ではないようです。とを使ってスクリプトを実行してみましたが、unbuffered
出力する前にstdbuf -i0 -o0 -e0
数字がtime
印刷され続けます。
答え1
それはい布材。
パイプラインの複数の部分が同時に実行されます。パイプ内でプロセスを同期/シリアル化する唯一のことはIOです。つまり、あるプロセスがパイプの次のプロセスに書き込み、次のプロセスが最初のプロセスによって書き込まれた内容を読み取ります。これ以外にも公演をしています。独立してお互い。
パイプ内のプロセス間で読み取りまたは書き込みが発生しないため、パイプの実行にかかる時間がsleep
最も長い呼び出し時間です。
また、書いたかもしれません
time ( foo.sh & bar.sh &; wait )
ここにリリースチャットで少し変更されたいくつかのサンプルスクリプト:
#!/bin/sh
# This is "foo.sh"
echo 1; sleep 1
echo 2; sleep 1
echo 3; sleep 1
echo 4
そして
#!/bin/sh
# This is "bar.sh"
sleep 2
while read line; do
echo "LL $line"
done
sleep 1
質問は「time ( sh foo.sh | sh bar.sh )
3+3 = 6秒ではなく4秒が返されるのはなぜですか?」です。
各コマンドが実行されたおおよその時間を含む何が起こっているかを確認するには、次のようにします(出力には私の説明が含まれます)。
$ time ( env PS4='$SECONDS foo: ' sh -x foo.sh | PS4='$SECONDS bar: ' sh -x bar.sh )
0 bar: sleep 2
0 foo: echo 1 ; The output is buffered
0 foo: sleep 1
1 foo: echo 2 ; The output is buffered
1 foo: sleep 1
2 bar: read line ; "bar" wakes up and reads the two first echoes
2 bar: echo LL 1
LL 1
2 bar: read line
2 bar: echo LL 2
LL 2
2 bar: read line ; "bar" waits for more
2 foo: echo 3 ; "foo" wakes up from its second sleep
2 bar: echo LL 3
LL 3
2 bar: read line
2 foo: sleep 1
3 foo: echo 4 ; "foo" does the last echo and exits
3 bar: echo LL 4
LL 4
3 bar: read line ; "bar" fails to read more
3 bar: sleep 1 ; ... and goes to sleep for one second
real 0m4.14s
user 0m0.00s
sys 0m0.10s
したがって、inへの最初の2つの呼び出しの出力バッファリングは、全体としてecho
パイプラインに6秒ではなく4秒かかりますfoo.sh
。
答え2
これは良い例ですか?
$ time perl -e 'alarm(3); 1 while 1;' | perl -e 'alarm(4); 1 while 1;'
Alarm clock
real 0m4.004s
user 0m6.992s
sys 0m0.004s
スクリプトbusyloopはそれぞれ3秒、4秒間持続し、並列実行のため、実際の時間は合計4秒、CPU時間は7秒かかりました。 (少なくともおおよそ)
またはこれ:
$ time ( sleep 2; echo) | ( read x; sleep 3 )
real 0m5.004s
user 0m0.000s
sys 0m0.000s
並列に実行されないため、合計時間は5秒です。すべての睡眠に費やされるため、CPU時間は使用されません。
答え3
可能であればsysdig
トラッカーを挿入できます。いつでもコードを変更して必要な書き込みを追加できるとします。/dev/null
echo '>::blah::' >/dev/null
foo.sh | bar.sh
echo '<::blah::' >/dev/null
(ただし、これは「単一操作」の要件を満たしていません。)その後、以下を介して項目を記録します。
$ sudo sysdig -w blalog "span.tags contains blah"
その後、期間をエクスポートするにはsysdigドラッグが必要になることがあります。
description = "Exports sysdig span tag durations";
short_description = "Export span tag durations.";
category = "Tracers";
args = {}
function on_init()
ftags = chisel.request_field("span.tags")
flatency = chisel.request_field("span.duration")
chisel.set_filter("evt.type=tracer and evt.dir=<")
return true
end
function on_event()
local tags = evt.field(ftags)
local latency = evt.field(flatency)
if latency then
print(tostring(tags) .. "\t" .. tonumber(latency) / 1e9)
end
return true
end
ディレクトリに保存されると、sysdig/chisels
ファイルは
spantagduration.lua
次のように使用できます。
$ sysdig -r blalog -c spantagduration
...
csysdig
またはJSON出力を使用できます。
答え4
とても面白いですね。古いスレッドというのはわかりましたが、私も同じ状況になっていました。エイリアシングが簡単な解決策であることがわかりました。少なくともこれはbashとfishで動作します。すべての殻についてはよくわかりません。
変える:time ( foo.sh | bar.sh )
努力する:
alias foobar="foo.sh | bar.sh" time foobar