Bir betiğin yürütme süresini etkili bir şekilde nasıl alabilirim?


340

Bir betiğin tamamlanma süresini görüntülemek istiyorum.

Şu anda yaptığım şey -

#!/bin/bash
date  ## echo the date at start
# the script contents
date  ## echo the date at end

Bu sadece şovun senaryosunun başlangıç ​​ve bitiş zamanını gösterir. İşlemci zamanı / saat zamanı, vb. Gibi ince taneli bir çıktı görüntülemek mümkün müdür?



Güncel kabul edilen cevabın zamansal dalgalanmalardan dolayı yeterli olmadığını düşünüyorum.
Léo Léopold Hertz 준영

1
@CiroSantilli 事件 事件 2016 六四 事件 法轮功 Önerilen iş parçacığının geçici olayların etkisini ortadan kaldırmak için hala yeterli olmadığını düşünüyorum.
Léo Léopold Hertz 준영

Ayrıca aşağıdakilerle ilgili: stackoverflow.com/questions/5152858/… veya bir şekilde unix.stackexchange.com/questions/334145/…
phk

Çalıştırabileceğiniz bir komut var ve ardından tüm unix komutlarını otomatik olarak zamanlıyor, nasıl etkinleştirileceğini unuttum?
pudra366

Yanıtlar:


449

Sadece timebetiği aradığınızda kullanın :

time yourscript.sh

61
Bu üç kez verir: real, userve sys. Bunların anlamları için buraya bakınız .
Garrett,

27
ve "gerçek" muhtemelen insanların bilmek istediği şeydir - "Gerçek duvar saati zamanıdır - aramanın başlangıcına kadar olan süre"
Brad Parks

3
Stdout'u bir dosyaya yakalamanın bir yolu var mı? Örneğin, şöyle bir şeytime $( ... ) >> out.txt
Jon,

1
Bunu komut satırındaki komut dizilerine de yapabilirsiniz, örneğin:time (command1 && command2)
meetar

1
Ya da senaryosunda şunu yapabilir: echo $ SECONDS bash olduğunu farz ederek ...
Crossfit_and_Beer

170

Bir timeseçenek değilse,

start=`date +%s`
stuff
end=`date +%s`

runtime=$((end-start))

30
Bunun sadece ikinci saniyelik hassasiyete ihtiyacınız olmadığında işe yaradığını unutmayın. Bazı kullanımlar için kabul edilebilir olabilir, diğerleri için değil. Biraz daha iyi hassasiyet için ( dateörneğin hala iki kez çağırıyorsunuz , örneğin pratikte en azından milisaniye hassasiyetini elde edebilirsiniz, muhtemelen daha az), kullanmayı deneyin date +%s.%N. ( %Nsaniye boyunca nanosaniyedir.)
12'de CVn

İyi bir nokta. Klavyeden çıktıktan hemen sonra düşündüm ama geri dönmedim. ^^ Ayrıca, OP'nin, “tarih” in çalışma zamanına birkaç milisaniye ekleyeceğini de unutmayın.
Rob Bos,

2
@ChrisH Oh. İşaret iyi; bash aritmetik genişlemesi yalnızca tam sayıdır. İki açık seçenek görüyorum; ya tarih formatı dizisindeki süreyi hendeleyin (ve sonuçtaki değeri çağdan beri nanosaniye olarak kabul edin), bu nedenle kullanın date +%s%Nya bcda gerçek çalışma zamanını jwchew'nin önerdiği iki değerden hesaplamak için daha yetenekli bir şey kullanın . Yine de, bu yaklaşımın bunu yapmanın yetersiz bir yol olduğunu düşünüyorum; timeyukarıda belirtilen sebeplerden dolayı, eğer mümkünse, çok daha iyidir.
Bir CVn

10
Sadece kurun bcve ardından şunu yapın:runtime=$( echo "$end - $start" | bc -l )
redolent

10
Komut dosyanız birkaç Dakika echo "Duration: $((($(date +%s)-$start)/60)) minutes
alıyorsa

75

Sadece timesbetiğinizden çıkarken argüman olmadan arayın .

kshVeya ile bunun yerine zshkullanabilirsiniz time. Bununla birlikte zsh, kullanıcı ve sistem CPU zamanına timeek olarak duvar saati süresini de verir .

Komut dosyanızın çıkış durumunu korumak için şunları yapabilirsiniz:

ret=$?; times; exit "$ret"

Veya ayrıca bir tuzak ekleyebilirsiniz EXIT:

trap times EXIT

Bu şekilde ne zaman kabuk çıkarsa çıkılsın ve çıkış durumu korunur.

$ bash -c 'trap times EXIT; : {1..1000000}'
0m0.932s 0m0.028s
0m0.000s 0m0.000s
$ zsh -c 'trap time EXIT; : {1..1000000}'
shell  0.67s user 0.01s system 100% cpu 0.677 total
children  0.00s user 0.00s system 0% cpu 0.677 total

Ayrıca hepsi dikkat bash, kshve zshbir var $SECONDSotomatik her saniye artırılır alır özel değişkeni. İkisinde de zshve ksh93bu değişken de kayan nokta (yapılabilir typeset -F SECONDSdaha hassas almak için). Bu sadece duvar saati, CPU zamanı değil.


12
Bu $ SECONDS değişkeni çok faydalıdır, teşekkürler!
andybuckley

Yaklaşımınız zamansal olayların etkisini ortadan kaldırıyor mu? - - timeYaklaşımınızın daha önce sunulan yaklaşıma yakın olduğunu düşünüyorum . - - timeitMATLAB kodunda sunulan yaklaşımın burada faydalı olabileceğini düşünüyorum.
Léo Léopold Hertz 준영

2
Merhaba, @ Stéphane Chazelas. Bilmiyorum timesduvar zamanı yok, değil mi? örneğin,bash -c 'trap times EXIT;sleep 2'
kullanıcı15964


32

Çoğunluğa geç kaldım, ancak başkalarının arayarak bu konuya karışması durumunda çözümümü (ikinci saniye hassasiyet için) göndermek istedim. Çıktı gün, saat, dakika ve son olarak saniye biçimindedir:

res1=$(date +%s.%N)

# do stuff in here

res2=$(date +%s.%N)
dt=$(echo "$res2 - $res1" | bc)
dd=$(echo "$dt/86400" | bc)
dt2=$(echo "$dt-86400*$dd" | bc)
dh=$(echo "$dt2/3600" | bc)
dt3=$(echo "$dt2-3600*$dh" | bc)
dm=$(echo "$dt3/60" | bc)
ds=$(echo "$dt3-60*$dm" | bc)

printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds

Umarım birileri bu yararlı buluyor!


1
Hesaplamaları yapmak için 'bc' kullanımı dışında başka (sadece bash) bir yol yoktur sanırım. BTW gerçekten iyi senaryo;)
tvl

1
FreeBSD'lerin dateikinci saniyenin kesinliğini desteklemediğini ve sadece Nzaman damgasını " tam anlamıyla" " ekleyeceğini unutmayın .
Anton Samsonov

1
Çok güzel bir senaryo, ancak bash'ım sonraki bölümle başa çıkmıyor. Ayrıca bc cinsinden / 1 'in etkin bir şekilde onu soyduğunu öğrendim, bu yüzden $ ds hesaplamasına @ / 1 @ ekledim ve şimdi çok güzel şeyler gösteriyor!
Daniel,

1
bc modulo destekler: dt2=$(echo "$dt%86400" | bc). Sadece söylüyorum ... BTW Formu tercih ediyorum dt2=$(bc <<< "${dt}%86400")ama bu tamamen kişisel.
Dani_l

16

Benim yöntemim bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"

Bu geçen süreyi gösterir, ancak soruda istendiği gibi "işlemci zamanı, G / Ç zamanı gibi ince taneli çıktılar" göstermez.
roaima

3
Soruya cevap olarak sorulanların bu çözümü faydalı bulmaları muhtemeldir. Yorumunuz OP'lerin sorusuna daha iyi cevap verecektir.
Mark

5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

end=$(date +%s.%N)    
runtime=$(python -c "print(${end} - ${start})")

echo "Runtime was $runtime"

Evet, bu Python'u çağırıyor, ancak bununla yaşayabilirseniz o zaman bu oldukça hoş ve özlü bir çözüm.


5
Bu pythonkomutu bir alt kabukta çalıştırmanın ve çıktısını okumanın çoğu mevcut sistemde milyonlarca nanosaniye alacağına dikkat edin . Çalıştırmak için aynı date.
Stéphane Chazelas

7
"Birkaç milyon nanosaniye" birkaç milisaniyedir. Bash betiğini zamanlayan insanlar genellikle bundan endişe duymazlar (megasaniye başına birkaç milyar komut dosyası çalıştırmazlarsa).
Zilk

2
Ayrıca, bir python işleminde hesaplama yapılsa bile, değerlerin python çağrılmadan önce tamamen toplandığına dikkat edin. Senaryonun yürütme süresi "milyonlarca nanosaniye" yükü olmadan doğru bir şekilde ölçülecektir.
Victor Schröder

5

Bu soru oldukça eski ama en sevdiğim yolu bulmaya çalışırken bu konu yükseldi ... ve kimsenin bahsetmediğine şaşırdım:

perf stat -r 10 -B sleep 1

'perf', çekirdek içerisinde 'tools / perf' başlığı altında bulunan ve genellikle ayrı bir paket olarak kurulabilen bir performans analiz aracıdır (CentOS'ta 'perf' ve Debian / Ubuntu'da 'linux-tools'). Linux Kernal perf Wiki hakkında daha fazla bilgi var.

'Perf stat' çalıştırmak, en sonunda ortalama yürütme süresi de dahil olmak üzere oldukça fazla ayrıntı verir:

1.002248382 seconds time elapsed                   ( +-  0.01% )

2
Nedir perf?
B Katmanı

@ B Katmanı - 'perf' kısa bir açıklamasını vermek için cevabımı düzenledi.
Zaahid

1
perf statşimdi "İstatistik toplama izniniz olmayabilir." Bazı komutları çalıştırmadığınız sürece sudo, hedef makineye tamamen sahip olmadığınız tüm senaryolarda işe yaramaz hale getirir.
alamar

4

Zamanlarını ölçmek için komutlardan önce eklenebilecek küçük bir kabuk işlevi:

tm() {
  s=$(date +%s)
  $@
  rc=$?
  echo "took $[$(date +%s)-$s] seconds. return code $rc" >&2
  return $rc
}

Ardından, komut dosyanızda veya komut satırınızda şöyle kullanın:

tm the_original_command with all its parameters

Çalıştığından s=$(date +%s000)emin değil , ilke denedim , milisaniye eklemek için değer olurdu .
loretoparisi

3

İşte Alex'in cevabının bir çeşidi. Sadece dakikaları ve saniyeleri önemsiyorum, fakat farklı formatlarda da olmasını istedim. Bu yüzden bunu yaptım:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")

1
Neden aşağı oy? Bir hatam var mı?
mpontillo

3
#!/bin/csh
#PBS -q glean
#PBS -l nodes=1:ppn=1
#PBS -l walltime=10:00:00
#PBS -o a.log
#PBS -e a.err
#PBS -V
#PBS -M shihcheng.guo@gmail.com
#PBS -m abe
#PBS -A k4zhang-group
START=$(date +%s)
for i in {1..1000000}
do
echo 1
done
END=$(date +%s)
DIFF=$(echo "$END - $START" | bc)
echo "It takes DIFF=$DIFF seconds to complete this task..."

7
Bu date, senaryodan önce ve sonra kullanılan ve aralarındaki farkı ortaya çıkaran verilen cevaplardan gerçekten farklı mı?
Eric Renouf,

2

Sadece bash kullanarak kabuk betiğinin bir kısmının (veya betiğin tamamı için geçen sürenin) zaman süresini ölçmek ve hesaplamak da mümkündür :

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

şimdi sadece ya şu farkı yazdırabilirsiniz:

echo "duration: $((end-start)) seconds."

Yalnızca artan süreye ihtiyacınız varsa şunları yapın:

echo "duration: $((SECONDS-start)) seconds elapsed.."

Süreyi bir değişkende de saklayabilirsiniz:

let diff=end-start

^ BU cevap millet. Ya da daha basit: sonunda $ SECONDS. BUILT-IN Bash değişkeni. Diğer cevapların hepsi sadece bu tekerleği yeniden icat etmek için ekstra iş yapıyor ...
Crossfit_and_Beer

2

Şahsen, tüm script kodumu şöyle bir "main" işlevine yerleştirmeyi seviyorum:

main () {
 echo running ...
}

# stuff ...

# calling the function at the very end of the script
time main

timeBu senaryoda komutu kullanmanın ne kadar kolay olduğuna dikkat edin . Açıkçası senaryo ayrıştırma zamanı dahil tam zamanı ölçmüyorsunuz, ancak çoğu durumda yeterince doğru buluyorum.


1
#!/bin/bash
begin=$(date +"%s")

Script

termin=$(date +"%s")
difftimelps=$(($termin-$begin))
echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."

1

SECONDSYalnızca ikinci seviye ayrıntı düzeyi ile sınırlı olan zamanlama işlevi herhangi bir harici komut kullanmaz:

time_it() {
  local start=$SECONDS ts ec
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Starting $*"
  "$@"; ec=$?
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Finished $*; elapsed = $((SECONDS-start)) seconds"
  # make sure to return the exit code of the command so that the caller can use it
  return "$ec"
}

Örneğin:

time_it sleep 5

verir

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished
sleep 5; elapsed = 5 seconds
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.