Bir boruyu nasıl zamanlayabilirim?


27

timeBirine diğerine boru çıkışı olan iki ayrı komuttan oluşan bir komut istiyorum . Örneğin, aşağıdaki iki komut dosyasını göz önünde bulundurun:

$ cat foo.sh
#!/bin/sh
sleep 4

$ cat bar.sh
#!/bin/sh
sleep 2

Şimdi, timegeçen süreyi nasıl rapor edebilirim foo.sh | bar.sh(ve evet, borunun burada bir anlamı olmadığını biliyorum, ama bu sadece bir örnek)? Boruları olmadan bir alt kabukta sırayla çalıştırırsam beklendiği gibi çalışır:

$ time ( foo.sh; bar.sh )

real    0m6.020s
user    0m0.010s
sys     0m0.003s

Ancak boru tesisatı yaparken çalışamıyorum:

$ 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

Benzer bir soru okudum ( Birden fazla komutta saat nasıl çalıştırılır VE zaman çıktısını dosyaya yazılır? ) Ve ayrıca tek başına timeçalıştırılabilir dosyayı da denedim :

$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00

Yalnızca boruyu çalıştıran üçüncü bir komut dosyası oluşturursam bile çalışmaz:

$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh

Ve sonra o zaman:

$ time baz.sh

real    0m4.009s
user    0m0.003s
sys     0m0.000s

İlginç bir şekilde, timeilk komut yapıldıktan hemen sonra çıkılmış gibi görünmüyor . Eğer değiştirirsem bar.sh:

#!/bin/sh
sleep 2
seq 1 5

Ve sonra timetekrar, timeçıktının basılmadan önce basılmasını bekliyordum , seqama değil:

$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5

real    0m4.005s
user    0m0.003s
sys     0m0.000s

Raporunu yazdırmadan önce bitmesini beklemesine rağmen timeyürütme süresini saymıyor gibi görünüyor 1 .bar.sh

Tüm testler bir Arch sistemi üzerinde yapıldı ve bash 4.4.12 (1) - release kullanılarak yapıldı. Bash'ı sadece proje için kullanabiliyorum, bu onun bir parçası, bu yüzden zshya da başka güçlü bir kabuğun etrafından dolansa bile, bu benim için uygun bir çözüm olmayacak.

Peki, bir takım bir takım komutların çalışması için geçen zamanı nasıl alabilirim? Ve biz onun yanındayken, neden çalışmıyor? timeİlk komut biter bitmez hemen çıkar gibi görünüyor . Niye ya?

Bireysel zamanları böyle bir şeyle alabileceğimi biliyorum:

( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time

Ama yine de her şeyi tek bir işlem olarak zamanlamanın mümkün olup olmadığını bilmek istiyorum.


1 Bu bir tampon sorunu görünmüyor, ben komut dosyalarını çalıştırarak çalıştı unbufferedve stdbuf -i0 -o0 -e0ve sayılar yine önce basıldığını timeçıktı.


Fiziksel bir kronometre ile denediniz mi?
pericynthion

@pericynthion evet sonunda yaptım. Bu aynı zamanda cevapların neyi açıkladığını da gösterdi: zaman gerçekten işe yarıyor ama (açıkçası yeterince ve farketmem gereken) boru hattındaki komutlar aynı anda çalışıyor, böylece alınan zaman aslında en yavaş olanın zamanı.
terdon

Yanıtlar:


33

O edilmektedir çalışma.

Bir boru hattının farklı bölümleri eşzamanlı olarak yürütülür. Boru hattındaki süreçleri senkronize eden / seri hale getiren tek şey, IO'dur; yani, bir boru hattındaki bir sonraki işleme yazılan ve birincisinin yazdıklarını okuyan bir süreç. Bunun dışında birbirlerinden bağımsız olarak yürütüyorlar .

Boru hattınızdaki işlemler arasında herhangi bir okuma ya da yazı olmadığından, boru hattını yürütmek için geçen süre en uzun sleepçağrıdır.

Sen de yazmış olabilirsin

time ( foo.sh & bar.sh &; wait )

Terdon , sohbette birkaç değişiklik modifiye edilmiş örnek komut dosyası yayınladı :

#!/bin/sh
# This is "foo.sh"
echo 1; sleep 1
echo 2; sleep 1
echo 3; sleep 1
echo 4

ve

#!/bin/sh
# This is "bar.sh"
sleep 2
while read line; do
  echo "LL $line"
done
sleep 1

Sorgu "neden time ( sh foo.sh | sh bar.sh )3 + 3 = 6 saniye yerine 4 saniye döndürüyor?"

Her bir komutun yürütüldüğü yaklaşık süre dahil olmak üzere, neler olduğunu görmek için, biri bunu yapabilir (çıkış açıklamalarımı içerir):

$ 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

Yani, boru hattı nedeniyle ilk iki aramaların çıktı buffering'e, 4 saniye değil 6 alır, sonuçlandırmak echoiçinde foo.sh.


1
@terdon değerleri toplamlar, ancak scriptler çok az kullanıcı ve sistem zamanı alır - sadece beklerler, bu sayılmaz (duvar saati hariç).
Stephen Kitt

2
Bourne kabuğu gibi bazı mermilerin ya da ksh93sadece boru hattının son bileşenini beklediğine dikkat edin ( sleep 3 | sleep 11 saniye sürecek). Bourne kabuğunun timeanahtar sözcüğü yoktur , ancak ksh93, çalıştırıldığında timetüm bileşenler beklenir.
Stéphane Chazelas,

3
Sadece bir bulmak için sürpriz olabilir söylüyorum sleep 10 | sleep 1bir saniye süre alır time sleep 10 | sleep 1ksh93 10 saniye sürer. Bourne kabuğu olarak time sleep 10 | sleep 1bir saniye alacağını, ancak (vakti çıktıları elde ederdiniz sleep 10sadece ve gelen /usr/bin/time) daha sonra mavi 9 saniye dışarı.
Stéphane Chazelas

1
Bu hiçbir şeyi korumakla ilgili değil. timeboru hattını doğru zamanlar, ancak ksh93 içindeki kabuğun davranışını değiştirir. (sleep 10 | sleep 1)1 saniye time (sleep 10 | sleep 1)sürer, 10 saniye sürer. { (sleep 10 | sleep 1); echo x; }çıkışları x1 saniye sonra, time { (sleep 10 | sleep 1); echo x; }çıkış x10 saniye sonra. Bu kodu bir işleve koyarsanız ve işleve zaman koyarsanız aynıdır.
Stéphane Chazelas,

1
İçinde olduğunu unutmayın ksh93benzeri içinde zsh( -o promptsubstburada), yapabileceğiniz typeset -F SECONDSdaha az almak için yaklaşık (POSIX saniye sayısını shhayır vardır SECONDS)
Stéphane Chazelas

10

Bu daha iyi bir örnek olur mu?

$ 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

Betikler, paralel yürütme nedeniyle gerçek zamanlı olarak toplam 4 saniye ve CPU süresinin 7 saniye sürmesiyle 3 ve 4 saniye (sırasıyla) meşgul. (en az yaklaşık olarak.)

Veya bu:

$ time ( sleep 2; echo) | ( read x; sleep 3 )

real    0m5.004s
user    0m0.000s
sys     0m0.000s

Bunlar paralel çalışmaz, bu nedenle toplam süre 5 saniyedir. Her şey uyuyarak geçti, bu yüzden CPU zamanı yoktu.


3

Eğer varsa sysdigsen izleyiciler ekleyebilir Eğer gerekli yazma eklemek için kodu değiştirebilir varsayarak, keyfi noktalarda/dev/null

echo '>::blah::' >/dev/null
foo.sh | bar.sh
echo '<::blah::' >/dev/null

(ancak bu "tek işlem" gerekliliğinizi yerine getirmez) ve ardından kayıtları

$ sudo sysdig -w blalog "span.tags contains blah"

ve sonra muhtemelen sadece süreleri dışa aktarmak için bir sysdig keski gerekir

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

bir kez kaydedilir hangi sysdig/chiselsdosya olarak dizinde spantagduration.luaolarak kullanılabilir

$ sysdig -r blalog -c spantagduration
...

Veya csysdigJSON çıktısı ile oynayabilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.