Linux shell獲取時間和時間間隔(ms級別)


 

說明:在進行一些性能測試的時候,有時候我們希望能計算一個程序運行的時間,有時候可能會自己寫一個shell腳本方便進行一些性能測試的控制(比如希望能運行N次取平均值等),總之,這其中有一個需求可能就是獲取一個時間戳或時間差。

 

1. Linux shell獲取時間的相關命令

time命令:獲取一個程序的執行時間,可以獲取到實際運行時間以及程序在用戶態和內核態分別的時間,大部分的性能測試,可能只需要關注實際時間。

time命令的使用就很簡單了,在原有的要運行的程序(可執行文件、腳本等任何可執行程序)之前加上time即可。

問題一:time命令的常用選項

使用time,常用的選項是:-f和-p。其中-f后面指定一個格式串,控制time的輸出的格式,默認的time輸出是real、user、sys三行以xmxxx.xxxs的格式輸出,通過-f可以控制。

-p選項也是格式,表示使用posix標准的格式,主要的區別是顯示的時間都是以s為單位的,具體關於格式的問題參考man time的幫助內容就知道了。

PS:-f選項沒法工作?弄不清楚為何-f和-o等選項都無法工作,知道的請補充。(-p是工作的)

另一種控制格式的方法是設置TIMEFORMAT環境變量,具體參考man time可以知道這些格式控制符分別表示什么。舉例如下:

 

  1. #time pwd  
  2. /home/sgeng2  
  3.   
  4. real    0m0.000s  
  5. user    0m0.000s  
  6. sys 0m0.000s  
  7. #export TIMEFORMAT="real time %E, user time %U,sys time %S"  
  8. #time pwd  
  9. /home/sgeng2  
  10. real time 0.000, user time 0.000,sys time 0.000  
  11. #time -p pwd  
  12. /home/sgeng2  
  13. real 0.00  
  14. user 0.00  
  15. sys 0.00  
  16. #  
#time pwd
/home/sgeng2

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#export TIMEFORMAT="real time %E, user time %U,sys time %S"
#time pwd
/home/sgeng2
real time 0.000, user time 0.000,sys time 0.000
#time -p pwd
/home/sgeng2
real 0.00
user 0.00
sys 0.00
#
PS:很奇怪,感覺time的有些格式和上面的選項一樣,好像不完全工作,總之,關系不大,格式並不重要,一般使用-p以秒作為單位就足夠了。

 

 

問題二:time命令的輸出的問題

上面已經說到,好像-o選項並不工作,那么,我們就只能自己想辦法了。有時候寫腳本,就希望把time的結果輸出到文件中,然后可能會根據time的輸出進行一些處理,比如提取出real時間等。顯然,大家能想到的是重定向了,至於重定向的使用這里顯然不准備討論(太復雜),只是提示一點:time命令的輸出結果是輸出到stderr的,不是stdout,所以進行重定向的時候要注意了。看懂下面的例子基本就能理解了:

 

  1. #time pwd  
  2. /home/sgeng2  
  3.   
  4. real    0m0.000s  
  5. user    0m0.000s  
  6. sys 0m0.000s  
  7. #time pwd > out.txt   
  8.   
  9. real    0m0.000s  
  10. user    0m0.000s  
  11. sys 0m0.000s  
  12. #cat out.txt   
  13. /home/sgeng2  
  14. #time pwd 2> out.txt   
  15. /home/sgeng2  
  16.   
  17. real    0m0.000s  
  18. user    0m0.000s  
  19. sys 0m0.000s  
  20. #cat out.txt   
  21. #(time pwd) 2> out.txt   
  22. /home/sgeng2  
  23. #cat out.txt   
  24.   
  25. real    0m0.000s  
  26. user    0m0.000s  
  27. sys 0m0.000s  
  28. #(time pwd) >& out.txt   
  29. #cat out.txt   
  30. /home/sgeng2  
  31.   
  32. real    0m0.000s  
  33. user    0m0.000s  
  34. sys 0m0.000s  
  35. #  
#time pwd
/home/sgeng2

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#time pwd > out.txt 

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#cat out.txt 
/home/sgeng2
#time pwd 2> out.txt 
/home/sgeng2

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#cat out.txt 
#(time pwd) 2> out.txt 
/home/sgeng2
#cat out.txt 

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#(time pwd) >& out.txt 
#cat out.txt 
/home/sgeng2

real	0m0.000s
user	0m0.000s
sys	0m0.000s
#
PS:這里更多的是涉及到的和重定向相關的內容,所以不會詳細分析每一個例子。說明的是注意time pwd 2> out.txt和(time pwd) 2> out.txt的區別,前面一個的含義是把pwd的結果stderr重定向到out.txt,相當於"time (pwd 2> out.txt)"的結果。

 

 

date命令

關於date命令的使用,百度一把一大堆,就不重復了,例如可以參考:http://xingfujie.blog.51cto.com/2791569/637223

這里只說明一下幾個常見的問題:

問題一:date的%s和%N

date中有很多控制格式的,其中%s是獲取當前時間距離1970-01-01 00:00:00 UTC的時間差。date的其它很多格式控制都是控制當前時間的輸出格式而已,比如只輸出時分秒,只輸出年月日等等,其中%N也是這一類,%N輸出的是當前時間的納秒部分,由於date並沒有毫秒等級別的輸出,所以在秒以下的內容都屬於納秒部分。所以從這個角度說,date是可以很精確的,可以達到納秒級別。

問題二:獲取一個時間戳

有時候會使用時間戳,或者隨機數,UUID這樣的東西,百度一下也有相關文章(比如搜索”shell date隨機數“等)。一般來說,可以用%s和%N組合的方式就沒問題,同一秒內,兩次運行%N肯定不會一樣,所以%s和%N組合能得到一個唯一數。

 

  1. #date +%s.%N  
  2. 1337435374.969263560  
  3. #date +%s+%N  
  4. 1337435377+310281496  
  5. #date +%s_%N  
  6. 1337435381_209334510  
  7. #date +%s_%N  
  8. 1337435383_169263078  
  9. #date +%s_%N  
  10. 1337435383_830009679  
  11. #  
#date +%s.%N
1337435374.969263560
#date +%s+%N
1337435377+310281496
#date +%s_%N
1337435381_209334510
#date +%s_%N
1337435383_169263078
#date +%s_%N
1337435383_830009679
#
PS:有時候可能希望用一個”唯一“的東西來對文件命名等,就可以加上時間戳了。

 

2. Linux shell獲取時間差(使用date命令)

至於使用time命令,其本身就是獲取一個時間差,但是顯然,time只適用於一些簡單的情況,因為后面的參數是可以執行的內容,有時候可能需要執行多條命令,用time就比較麻煩。

(1) 秒為單位

date獲取的是”當前時間“,顯然,兩次運行date就可以得到一個時間差,理論上,可以使用很多格式來表示date的輸出,從而計算時間差,但是,顯然,最直接的方式就是使用%s了,這樣直接就可以計算出一個時間差,不需要那么復雜的shell字符串處理了。如下:

 

  1. #start=$(date +%s) && sleep 2 && end=$(date +%s) && echo $(( $end - $start ))  
  2. 2  
  3. #start=$(date +%s) && sleep 3 && end=$(date +%s) && echo $(( $end - $start ))  
  4. 3  
  5. #  
#start=$(date +%s) && sleep 2 && end=$(date +%s) && echo $(( $end - $start ))
2
#start=$(date +%s) && sleep 3 && end=$(date +%s) && echo $(( $end - $start ))
3
#

當然,這里是在terminal中測試的,所以用&&連續執行這些命令,對於腳本,一行一行的寫就很好了。。。。

 

 

  1. start=$(date +%s)  
  2. ...what to do for timing...  
  3. end=$(date +%s)  
  4. time=$(( $end - $start ))  
  5. echo $time  
start=$(date +%s)
...what to do for timing...
end=$(date +%s)
time=$(( $end - $start ))
echo $time

 

(2) ms為單位

更多的性能測試等場合獲取時間差,有可能希望精確到ms。事實上,使用date是可以達到ms的。直接上代碼吧。

 

  1. #! /bin/bash  
  2. #filename: test.sh  
  3.   
  4.   
  5. # arg1=start, arg2=end, format: %s.%N  
  6. function getTiming() {  
  7.     start=$1  
  8.     end=$2  
  9.      
  10.     start_s=$(echo $start | cut -d '.' -f 1)  
  11.     start_ns=$(echo $start | cut -d '.' -f 2)  
  12.     end_s=$(echo $end | cut -d '.' -f 1)  
  13.     end_ns=$(echo $end | cut -d '.' -f 2)  
  14.   
  15.   
  16. # for debug..  
  17. #    echo $start  
  18. #    echo $end  
  19.   
  20.   
  21.     time=$(( ( 10#$end_s - 10#$start_s ) * 1000 + ( 10#$end_ns / 1000000 - 10#$start_ns / 1000000 ) ))  
  22.   
  23.   
  24.     echo "$time ms"  
  25. }  
  26.   
  27.   
  28.   
  29.   
  30. echo "This is only a test to get a ms level time duration..."  
  31. start=$(date +%s.%N)  
  32. ls >& /dev/null    # hey, be quite, do not output to console....  
  33. end=$(date +%s.%N)  
  34.   
  35.   
  36. getTiming $start $end  
#! /bin/bash
#filename: test.sh


# arg1=start, arg2=end, format: %s.%N
function getTiming() {
    start=$1
    end=$2
   
    start_s=$(echo $start | cut -d '.' -f 1)
    start_ns=$(echo $start | cut -d '.' -f 2)
    end_s=$(echo $end | cut -d '.' -f 1)
    end_ns=$(echo $end | cut -d '.' -f 2)


# for debug..
#    echo $start
#    echo $end


    time=$(( ( 10#$end_s - 10#$start_s ) * 1000 + ( 10#$end_ns / 1000000 - 10#$start_ns / 1000000 ) ))


    echo "$time ms"
}




echo "This is only a test to get a ms level time duration..."
start=$(date +%s.%N)
ls >& /dev/null    # hey, be quite, do not output to console....
end=$(date +%s.%N)


getTiming $start $end
PS:這個代碼是一個簡單的測試,可以獲取到ls命令執行的時間,相信其執行時間已經夠短了,如果你需要獲取的時間差在ms以下,相信你不會使用shell了,嘿嘿,自然要靠C去弄了。

 

結果如下:

 

  1. #./test.sh   
  2. This is only a test to get a ms level time duration...  
  3. 3 ms  
  4. #./test.sh   
  5. This is only a test to get a ms level time duration...  
  6. 2 ms  
  7. #./test.sh   
  8. This is only a test to get a ms level time duration...  
  9. 2 ms  
  10. #  
#./test.sh 
This is only a test to get a ms level time duration...
3 ms
#./test.sh 
This is only a test to get a ms level time duration...
2 ms
#./test.sh 
This is only a test to get a ms level time duration...
2 ms
#

 

還滿意吧,能獲取到這么短的時間。當然,理論上可以獲取到以ns為單位的時間差,但是,個人覺得用shell去弄這么小的時間差,你覺得准確么。。。

說明:

上面的代碼的思路,其實很簡單了,%s為距離標准時間的秒數,%N為當前時間的秒以下的部分,那么顯然,%s和%N就表示了當前時間的完整時間戳,兩個時間戳就差值就是時間差,問題就是如何處理的問題,大概就是:先使用%s.%N的格式獲取到start和end時間,然后利用cut命令從start和end中獲取到“.“前面的秒的部分和后面的納秒的部分(說明:這里的在%s和%N之間插入點,只是作為分隔的作用,任何可能的字符都是可以的,只要能被cut分開就行);然后,用秒的部分相減,得到秒的差值,轉換為毫秒的差值;然后,把納秒的部分轉換為毫秒之后求差值(可能為負數);最后,兩個差值相加就是真正的以毫秒為單位的差值了。很容易理解,關鍵是cut的使用,對於shell高手來說,應該有很多方法可以對字符串提取,但是對於我這樣的非shell高手,要自己用awk或sed什么的寫一個提取的正則,還是很有難度,還好找到了cut命令,能很容易的對這種字符串進行提取。所以:這里的方法僅供參考。。。

關於代碼中的“10#”,這是表示后面的數是10進制的數字,之所以需要這個是因為這里的納秒的前面是以0開頭的,shell中好像以0開頭會默認認為是八進制,導致運行報錯,總之,百度一下就找到了原因,這里就索性把所有的數字都加上了10#,表示都是10進制。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM