วิธีการคำนวณเวลาที่ผ่านไปในสคริปต์ทุบตี?


224

ฉันพิมพ์เวลาเริ่มต้นและสิ้นสุดโดยใช้date +"%T"ซึ่งจะมีผลดังนี้:

10:33:56
10:36:10

ฉันจะคำนวณและพิมพ์ความแตกต่างระหว่างสองสิ่งนี้ได้อย่างไร

ฉันต้องการที่จะได้รับสิ่งที่ชอบ:

2m 14s

5
ในบันทึกอื่นคุณไม่สามารถใช้timeคำสั่งได้หรือไม่
anishsane

ใช้เวลา unix แทนdate +%sแล้วลบเพื่อให้ได้ส่วนต่างเป็นวินาที
roblogic

คำตอบ:


476

Bash มีSECONDSตัวแปรบิวด์อินที่ใช้งานง่ายซึ่งติดตามจำนวนวินาทีที่ผ่านไปตั้งแต่เริ่มต้นเชลล์ ตัวแปรนี้จะเก็บรักษาคุณสมบัติของมันไว้เมื่อได้รับมอบหมายและค่าที่ส่งคืนหลังจากการมอบหมายคือจำนวนวินาทีตั้งแต่การกำหนดบวกค่าที่กำหนด

ดังนั้นคุณสามารถตั้งค่าSECONDSเป็น 0 ก่อนที่จะเริ่มเหตุการณ์ที่เกิดขึ้นเพียงแค่อ่านSECONDSหลังจากเหตุการณ์และทำเลขคณิตเวลาก่อนที่จะแสดง

SECONDS=0
# do some work
duration=$SECONDS
echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."

เนื่องจากโซลูชันนี้ไม่ได้ขึ้นอยู่กับdate +%s(ซึ่งเป็นส่วนขยายของ GNU) จึงเป็นระบบพกพาสำหรับทุกระบบที่สนับสนุนโดย Bash


59
+1 อนึ่งคุณสามารถหลอกให้เข้าไปดำเนินการทางคณิตศาสตร์เวลาสำหรับคุณโดยการเขียนdate date -u -d @"$diff" +'%-Mm %-Ss'(นั่นแปล$diffว่าวินาทีนับตั้งแต่ยุคและคำนวณนาทีและวินาทีใน UTC) นั่นอาจไม่ใช่สิ่งที่สง่างามกว่านี้อีก :-P
ruakh

13
ดีและเรียบง่าย หากมีคนต้องการเวลา:echo "$(($diff / 3600)) hours, $((($diff / 60) % 60)) minutes and $(($diff % 60)) seconds elapsed."
chus

6
ควรอ่าน$(date -u +%s)เพื่อป้องกันสภาพการแข่งขันในวันที่มีการเปลี่ยนเวลาออมแสง และระวังวินาทีก้าวกระโดดที่ชั่วร้าย)
Tino

3
@ daniel-kamil-kozar Nice find. อย่างไรก็ตามนี่มันค่อนข้างบอบบาง มีเพียงตัวแปรเดียวเท่านั้นดังนั้นจึงไม่สามารถใช้ซ้ำเพื่อวัดประสิทธิภาพและยิ่งแย่ลงไปกว่านั้นหลังจากที่unset SECONDSมันหายไป:echo $SECONDS; unset SECONDS; SECONDS=0; sleep 3; echo $SECONDS
Tino

6
@ParthianShot: ฉันขอโทษที่คุณคิดอย่างนั้น อย่างไรก็ตามฉันเชื่อว่าคำตอบนี้เป็นสิ่งที่คนส่วนใหญ่ต้องการในขณะที่ค้นหาคำตอบสำหรับคำถามเช่นนี้เพื่อวัดว่าเวลาผ่านไปนานเท่าใดระหว่างเหตุการณ์ไม่ใช่เพื่อคำนวณเวลา
Daniel Kamil Kozar

99

วินาที

ในการวัดเวลาที่ผ่านไป (เป็นวินาที) เราต้องการ:

  • จำนวนเต็มที่แทนจำนวนวินาทีที่ผ่านไปและ
  • วิธีการแปลงจำนวนเต็มเป็นรูปแบบที่ใช้งานได้

ค่าจำนวนเต็มของวินาทีที่ผ่านไป:

  • มีวิธีทุบตีภายในสองวิธีในการค้นหาค่าจำนวนเต็มสำหรับจำนวนวินาทีที่ผ่านไป:

    1. ตัวแปร Bash SECONDS (หาก SECONDS ไม่ได้ถูกตั้งค่ามันจะสูญเสียคุณสมบัติพิเศษ)

      • การตั้งค่า SECONDS เป็น 0:

        SECONDS=0
        sleep 1  # Process to execute
        elapsedseconds=$SECONDS
      • การเก็บค่าของตัวแปรSECONDSเมื่อเริ่มต้น:

        a=$SECONDS
        sleep 1  # Process to execute
        elapsedseconds=$(( SECONDS - a ))
    2. ตัวเลือก Bash printf %(datefmt)T:

      a="$(TZ=UTC0 printf '%(%s)T\n' '-1')"    ### `-1`  is the current time
      sleep 1                                  ### Process to execute
      elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - a ))

แปลงจำนวนเต็มเป็นรูปแบบที่ใช้งานได้

bash internal printfสามารถทำได้โดยตรง:

$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45

เหมือนกับ

$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34

แต่สิ่งนี้จะล้มเหลวเป็นระยะเวลานานกว่า 24 ชั่วโมงในขณะที่เราพิมพ์เวลา wallclock ไม่ใช่ระยะเวลา:

$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24

สำหรับผู้ชื่นชอบรายละเอียดจากbash-hackers.org :

%(FORMAT)Tstrftime(3)เอาท์พุทสตริงวันเวลาที่เกิดจากการใช้รูปแบบเป็นสตริงรูปแบบ อาร์กิวเมนต์ที่เกี่ยวข้องคือจำนวนวินาทีตั้งแต่Epochหรือ -1 (เวลาปัจจุบัน) หรือ -2 (เวลาเริ่มต้นเชลล์) หากไม่มีการระบุอาร์กิวเมนต์ที่สอดคล้องกันเวลาปัจจุบันจะถูกใช้เป็นค่าเริ่มต้น

ดังนั้นคุณอาจต้องการโทรไปยังtextifyDuration $elpasedsecondsที่ที่textifyDurationยังมีการใช้งานการพิมพ์ระยะเวลา:

textifyDuration() {
   local duration=$1
   local shiff=$duration
   local secs=$((shiff % 60));  shiff=$((shiff / 60));
   local mins=$((shiff % 60));  shiff=$((shiff / 60));
   local hours=$shiff
   local splur; if [ $secs  -eq 1 ]; then splur=''; else splur='s'; fi
   local mplur; if [ $mins  -eq 1 ]; then mplur=''; else mplur='s'; fi
   local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
   if [[ $hours -gt 0 ]]; then
      txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
   elif [[ $mins -gt 0 ]]; then
      txt="$mins minute$mplur, $secs second$splur"
   else
      txt="$secs second$splur"
   fi
   echo "$txt (from $duration seconds)"
}

วันที่ GNU

เพื่อให้ได้เวลาที่กำหนดเราควรใช้เครื่องมือภายนอก (วันที่ GNU) ในหลายวิธีเพื่อให้ได้ความยาวเกือบหนึ่งปีและรวมถึง Nanoseconds

คณิตศาสตร์ภายในวันที่

ไม่จำเป็นต้องใช้เลขคณิตภายนอกทำทุกอย่างในขั้นตอนเดียวdate:

date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"

ใช่มี0ศูนย์ในสตริงคำสั่ง มันเป็นสิ่งจำเป็น

นั่นคือสมมติว่าคุณสามารถเปลี่ยนdate +"%T"คำสั่งเป็นdate +"%s"คำสั่งดังนั้นค่าจะถูกเก็บไว้ (พิมพ์) ในไม่กี่วินาที

โปรดทราบว่าคำสั่งถูก จำกัด ที่:

  • ค่าบวกของ$StartDateและ$FinalDateวินาที
  • ในมูลค่า$FinalDateที่มีขนาดใหญ่ (ในเวลาต่อมา) $StartDateมากกว่า
  • เวลาต่างกันน้อยกว่า 24 ชั่วโมง
  • คุณยอมรับรูปแบบผลลัพธ์ด้วยชั่วโมงนาทีและวินาที เปลี่ยนง่ายมาก
  • เป็นที่ยอมรับได้ในการใช้เวลา -u UTC เพื่อหลีกเลี่ยง "DST" และการแก้ไขเวลาท้องถิ่น

หากคุณต้องใช้10:33:56สตริงก็แค่แปลงเป็นวินาที
เช่นกันคำว่าวินาทีอาจถูกย่อเป็นวินาที:

string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"

โปรดทราบว่าการแปลงเวลาเป็นวินาที (ตามที่แสดงด้านบน) สัมพันธ์กับการเริ่มต้นของวันนี้ (วันนี้)


แนวคิดสามารถขยายเป็นนาโนวินาทีเช่นนี้:

string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"

หากจำเป็นต้องคำนวณความแตกต่างของเวลาที่นานขึ้น (สูงสุด 364 วัน) เราต้องใช้การเริ่มต้น (บางส่วน) เป็นข้อมูลอ้างอิงและค่ารูปแบบ%j(จำนวนวันในปี):

คล้ายกับ:

string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"

Output:
026 days 00:02:14.340003400

น่าเศร้าในกรณีนี้เราต้องลบ1ONE ด้วยตนเองจากจำนวนวัน คำสั่ง date ดูวันแรกของปีเป็น 1 ไม่ยาก ...

a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"



การใช้จำนวนวินาทีที่ยาวนานนั้นถูกต้องและมีการบันทึกไว้ที่นี่:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date


วันที่ไม่ว่าง

เครื่องมือที่ใช้ในอุปกรณ์ขนาดเล็ก (มีขนาดเล็กมากปฏิบัติการติดตั้ง): Busybox

สร้างลิงก์ไปยัง busybox เรียกว่าวันที่:

$ ln -s /bin/busybox date

ใช้แล้วโดยเรียกสิ่งนี้date(วางไว้ในไดเรกทอรีรวมเส้นทาง)

หรือสร้างชื่อแทนเช่น:

$ alias date='busybox date'

วันที่ Busybox มีตัวเลือกที่ดี: -D เพื่อรับรูปแบบของเวลาอินพุต ที่เปิดขึ้นหลายรูปแบบเพื่อใช้เป็นเวลา การใช้ตัวเลือก -D เราสามารถแปลงเวลา 10:33:56 โดยตรง:

date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"

และอย่างที่คุณเห็นจากผลลัพธ์ของคำสั่งด้านบนวันนั้นจะถือว่าเป็น "วันนี้" ในการรับเวลาเริ่มต้นในยุค:

$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

วันที่ไม่ว่างสามารถรับเวลาได้ (ในรูปแบบด้านบน) โดยไม่มี -D:

$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56

และรูปแบบเอาต์พุตอาจเป็นวินาทีตั้งแต่กาล

$ date -u -d "1970.01.01-$string1" +"%s"
52436

สำหรับทั้งสองครั้งและคณิตศาสตร์ทุบตีเล็ก ๆ น้อย ๆ (busybox ไม่สามารถทำคณิตศาสตร์ได้):

string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))

หรือจัดรูปแบบ:

$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14

6
นี่เป็นคำตอบที่น่าอัศจรรย์ครอบคลุมหลายฐานและมีคำอธิบายที่ดี ขอบคุณ!
Devy

ฉันต้องค้นหา%(FORMAT)Tสตริงที่ส่งผ่านไปยังprintfคำสั่งที่รวมอยู่ใน bash จากbash-hackers.org : %(FORMAT)Tส่งออกสตริงวันที่และเวลาซึ่งเป็นผลมาจากการใช้FORMATเป็นสตริงรูปแบบสำหรับ strftime (3) อาร์กิวเมนต์ที่เกี่ยวข้องคือจำนวนวินาทีตั้งแต่ Epoch หรือ -1 (เวลาปัจจุบัน) หรือ -2 (เวลาเริ่มต้นเชลล์) ถ้าไม่มีข้อโต้แย้งที่สอดคล้องกันคือวัสดุสิ้นเปลืองเวลาปัจจุบันถูกนำมาใช้เป็นค่าเริ่มต้น นั่นเป็นความลับ ในกรณีนี้%sตามที่กำหนดให้strftime(3)คือ "จำนวนวินาทีตั้งแต่ยุค"
David Tonhofer

35

นี่คือวิธีที่ฉันทำ:

START=$(date +%s);
sleep 1; # Your stuff
END=$(date +%s);
echo $((END-START)) | awk '{print int($1/60)":"int($1%60)}'

ง่ายมากใช้เวลาจำนวนวินาทีในการเริ่มต้นจากนั้นใช้จำนวนวินาทีที่สิ้นสุดและพิมพ์ส่วนต่างเป็นนาที: วินาที


6
มันแตกต่างจากโซลูชันของฉันอย่างไร? ฉันไม่เห็นประโยชน์จากการโทรawkในกรณีนี้เนื่องจาก Bash จัดการเลขคณิตจำนวนเต็มได้ดีเท่ากัน
Daniel Kamil Kozar

1
คำตอบของคุณก็ถูกต้องเช่นกัน บางคนอย่างฉันชอบทำงานกับ awk มากกว่ากับ bash ที่ไม่สอดคล้องกัน
Dorian

2
คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับความไม่สอดคล้องกันใน Bash เกี่ยวกับเลขคณิตจำนวนเต็มได้ไหม? ฉันต้องการทราบเพิ่มเติมเกี่ยวกับเรื่องนี้เนื่องจากฉันไม่ได้ตระหนักถึงสิ่งใด
Daniel Kamil Kozar

12
ฉันแค่มองหาสิ่งนี้ ฉันไม่เข้าใจคำวิจารณ์สำหรับคำตอบนี้ ฉันชอบที่จะเห็นทางออกมากกว่าหนึ่งปัญหา และฉันเป็นคนหนึ่งที่ชอบawkสั่งให้ทุบตี (ถ้าไม่มีอะไรอื่นเพราะ awk ทำงานในเชลล์อื่น) ฉันชอบวิธีนี้ดีกว่า แต่นั่นเป็นความเห็นส่วนตัวของฉัน
rpsml

4
ศูนย์นำหน้า: echo $ ((END-START)) | awk '{printf "% 02d:% 02d \ n", int ($
Jon Strayer

17

ตัวเลือกอื่นคือใช้datediffจากdateutils( http://www.fresse.org/dateutils/#datediff ):

$ datediff 10:33:56 10:36:10
134s
$ datediff 10:33:56 10:36:10 -f%H:%M:%S
0:2:14
$ datediff 10:33:56 10:36:10 -f%0H:%0M:%0S
00:02:14

gawkนอกจากนี้คุณยังสามารถใช้ mawk1.3.4 นอกจากนี้ยังมีstrftimeและmktimeแต่รุ่นเก่าmawkและnawkทำไม่ได้

$ TZ=UTC0 awk 'BEGIN{print strftime("%T",mktime("1970 1 1 10 36 10")-mktime("1970 1 1 10 33 56"))}'
00:02:14

หรือนี่คือวิธีอื่นในการทำกับ GNU date:

$ date -ud@$(($(date -ud'1970-01-01 10:36:10' +%s)-$(date -ud'1970-01-01 10:33:56' +%s))) +%T
00:02:14

1
ฉันกำลังมองหาบางอย่างเช่นdate วิธีGNU ของคุณ อัจฉริยภาพ
Haohmaru

โปรดลบความคิดเห็นของฉัน ... ขออภัย
Bill Gale

หลังจากติดตั้งdateutilsผ่านฉลาดไม่มีdatediffคำสั่ง - dateutils.ddiffมีการใช้งาน
Suzana

12

ฉันต้องการเสนอวิธีอื่นที่หลีกเลี่ยงการเรียกใช้dateคำสั่ง อาจเป็นประโยชน์ในกรณีที่คุณได้รวบรวมการประทับเวลาใน%Tรูปแบบวันที่:

ts_get_sec()
{
  read -r h m s <<< $(echo $1 | tr ':' ' ' )
  echo $(((h*60*60)+(m*60)+s))
}

start_ts=10:33:56
stop_ts=10:36:10

START=$(ts_get_sec $start_ts)
STOP=$(ts_get_sec $stop_ts)
DIFF=$((STOP-START))

echo "$((DIFF/60))m $((DIFF%60))s"

เราสามารถจัดการมิลลิวินาทีในลักษณะเดียวกัน

ts_get_msec()
{
  read -r h m s ms <<< $(echo $1 | tr '.:' ' ' )
  echo $(((h*60*60*1000)+(m*60*1000)+(s*1000)+ms))
}

start_ts=10:33:56.104
stop_ts=10:36:10.102

START=$(ts_get_msec $start_ts)
STOP=$(ts_get_msec $stop_ts)
DIFF=$((STOP-START))

min=$((DIFF/(60*1000)))
sec=$(((DIFF%(60*1000))/1000))
ms=$(((DIFF%(60*1000))%1000))

echo "${min}:${sec}.$ms"

1
มีวิธีจัดการกับมิลลิวินาทีหรือไม่เช่น 10: 33: 56.104
Umesh

การจัดการมิลลิวินาทีทำงานผิดปกติหากฟิลด์มิลลิวินาทีเริ่มต้นด้วยศูนย์ เช่น ms = "033" จะให้ตัวเลขต่อท้ายเป็น "027" เนื่องจากฟิลด์ ms จะถูกตีความว่าเป็นฐานแปด การเปลี่ยน "echo" ... "+ ms))" เป็น ... "+ $ {ms ## + (0)}))" จะแก้ไขปัญหานี้ตราบใดที่ "shopt -s extglob" ปรากฏที่ใดที่หนึ่งเหนือกว่านี้ใน ต้นฉบับ หนึ่งอาจจะตัดเลขศูนย์จาก H, ม. ชั้นนำและ s เช่นกัน ...
เอริคทาวเวอร์

3
echo $(((60*60*$((10#$h)))+(60*$((10#$m)))+$((10#$s))))ทำงานให้ฉันตั้งแต่ฉันได้รับvalue too great for base (error token is "08")
Niklas

6

นี่คือเวทมนตร์บางส่วน:

time1=14:30
time2=$( date +%H:%M ) # 16:00
diff=$(  echo "$time2 - $time1"  | sed 's%:%+(1/60)*%g' | bc -l )
echo $diff hours
# outputs 1.5 hours

sedแทนที่:ด้วยสูตรให้แปลงเป็น 1/60 จากนั้นทำการคำนวณเวลาที่ทำbc


5

ณ วันที่ (GNU coreutils) 7.4 คุณสามารถใช้ -d เพื่อทำเลขคณิต:

$ date -d -30days
Sat Jun 28 13:36:35 UTC 2014

$ date -d tomorrow
Tue Jul 29 13:40:55 UTC 2014

หน่วยที่คุณสามารถใช้ได้คือวันปีเดือนชั่วโมงนาทีและวินาที:

$ date -d tomorrow+2days-10minutes
Thu Jul 31 13:33:02 UTC 2014

3

ติดตามจากคำตอบของ Daniel Kamil Kozar เพื่อแสดงชั่วโมง / นาที / วินาที:

echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

ดังนั้นสคริปต์เต็มรูปแบบจะเป็น:

date1=$(date +"%s")
date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"

3

หรือห่อมันขึ้นเล็กน้อย

alias timerstart='starttime=$(date +"%s")'
alias timerstop='echo seconds=$(($(date +"%s")-$starttime))'

จากนั้นใช้งานได้

timerstart; sleep 2; timerstop
seconds=2

3

นี่คือโซลูชันที่ใช้เฉพาะdateความสามารถของคำสั่งที่ใช้ "ผ่านมา" และไม่ใช้ตัวแปรตัวที่สองเพื่อเก็บเวลาที่เสร็จสิ้น:

#!/bin/bash

# save the current time
start_time=$( date +%s.%N )

# tested program
sleep 1

# the current time after the program has finished
# minus the time when we started, in seconds.nanoseconds
elapsed_time=$( date +%s.%N --date="$start_time seconds ago" )

echo elapsed_time: $elapsed_time

สิ่งนี้ให้:

$ ./time_elapsed.sh 
elapsed_time: 1.002257120

2
% start=$(date +%s)
% echo "Diff: $(date -d @$(($(date +%s)-$start)) +"%M minutes %S seconds")"
Diff: 00 minutes 11 seconds

6
การรู้ว่าคำตอบนี้เป็นประโยชน์หรือไม่ถ้ามีคำตอบอื่น ๆ ก็ไม่ควรทำ
Louis

ช่วยให้คุณสามารถส่งออกระยะเวลาในรูปแบบที่อนุญาตโดยdateง่าย
Ryan C. Thompson

2

date สามารถให้ความแตกต่างและจัดรูปแบบให้คุณ (ตัวเลือก OS X ที่แสดง)

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) +%T
# 00:02:14

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss'
# 0h 2m 14s

การประมวลผลสตริงบางตัวสามารถลบค่าว่างเหล่านั้นได้

date -ujf%s $(($(date -jf%T "10:36:10" +%s) - $(date -jf%T "10:33:56" +%s))) \
    +'%-Hh %-Mm %-Ss' | sed "s/[[:<:]]0[hms] *//g"
# 2m 14s

สิ่งนี้จะไม่ทำงานหากคุณวางไว้ก่อนหน้านี้ก่อน หากคุณต้องการจัดการกับมันให้เปลี่ยน$(($(date ...) - $(date ...)))เป็น$(echo $(date ...) - $(date ...) | bc | tr -d -)


1

ฉันต้องการสคริปต์ต่างเวลาสำหรับใช้กับmencoder(มัน--endposสัมพันธ์กับ) และโซลูชันของฉันคือเรียกสคริปต์ Python:

$ ./timediff.py 1:10:15 2:12:44
1:02:29

สนับสนุนเศษส่วนของวินาทีด้วย:

$ echo "diff is `./timediff.py 10:51.6 12:44` (in hh:mm:ss format)"
diff is 0:01:52.4 (in hh:mm:ss format)

และสามารถบอกคุณได้ว่าความแตกต่างระหว่าง 200 กับ 120 คือ 1h 20m:

$ ./timediff.py 120:0 200:0
1:20:0

และสามารถแปลงจำนวนวินาที (หรือเศษส่วน) เป็นวินาทีหรือนาทีหรือชั่วโมงเป็น hh: mm: ss

$ ./timediff.py 0 3600
1:00:0
$ ./timediff.py 0 3.25:0:0
3:15:0

timediff.py:

#!/usr/bin/python

import sys

def x60(h,m):
    return 60*float(h)+float(m)

def seconds(time):
    try:
       h,m,s = time.split(':')
       return x60(x60(h,m),s)
    except ValueError:
       try:
          m,s = time.split(':')
          return x60(m,s)
       except ValueError:
          return float(time)

def difftime(start, end):
    d = seconds(end) - seconds(start)
    print '%d:%02d:%s' % (d/3600,d/60%60,('%02f' % (d%60)).rstrip('0').rstrip('.'))

if __name__ == "__main__":
   difftime(sys.argv[1],sys.argv[2])

1

ด้วย GNU units:

$ units
2411 units, 71 prefixes, 33 nonlinear units
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: s
    * 134
    / 0.0074626866
You have: (10hr+36min+10s)-(10hr+33min+56s)
You want: min
    * 2.2333333
    / 0.44776119

1

วิธีแก้ปัญหาของ Generalis @ nisetama โดยใช้วันที่ GNU (Ubuntu ที่เชื่อถือได้ 14.04 LTS):

start=`date`
# <processing code>
stop=`date`
duration=`date -ud@$(($(date -ud"$stop" +%s)-$(date -ud"$start" +%s))) +%T`

echo $start
echo $stop
echo $duration

ผลผลิต:

Wed Feb 7 12:31:16 CST 2018
Wed Feb 7 12:32:25 CST 2018
00:01:09

1
#!/bin/bash

START_TIME=$(date +%s)

sleep 4

echo "Total time elapsed: $(date -ud "@$(($(date +%s) - $START_TIME))" +%T) (HH:MM:SS)"
$ ./total_time_elapsed.sh 
Total time elapsed: 00:00:04 (HH:MM:SS)

1

กำหนดฟังก์ชั่นนี้ (พูดใน ~ / .bashrc):

time::clock() {
    [ -z "$ts" ]&&{ ts=`date +%s%N`;return;}||te=`date +%s%N`
    printf "%6.4f" $(echo $((te-ts))/1000000000 | bc -l)
    unset ts te
}

ตอนนี้คุณสามารถวัดเวลาของส่วนต่างๆของสคริปต์ของคุณ:

$ cat script.sh
# ... code ...
time::clock
sleep 0.5
echo "Total time: ${time::clock}"
# ... more code ...

$ ./script.sh
Total time: 0.5060

มีประโยชน์มากในการค้นหาคอขวดการดำเนินการ


0

ฉันรู้ว่านี่คือโพสต์ที่เก่ากว่า แต่ฉันพบมันในวันนี้ในขณะที่ทำงานกับสคริปต์ที่จะใช้วันที่และเวลาจากไฟล์บันทึกและคำนวณเดลต้า สคริปต์ด้านล่างนั้นเกินความจริงอย่างแน่นอนและฉันขอแนะนำให้ตรวจสอบตรรกะและคณิตศาสตร์ของฉัน

#!/bin/bash

dTime=""
tmp=""

#firstEntry="$(head -n 1 "$LOG" | sed 's/.*] \([0-9: -]\+\).*/\1/')"
firstEntry="2013-01-16 01:56:37"
#lastEntry="$(tac "$LOG" | head -n 1 | sed 's/.*] \([0-9: -]\+\).*/\1/')"
lastEntry="2014-09-17 18:24:02"

# I like to make the variables easier to parse
firstEntry="${firstEntry//-/ }"
lastEntry="${lastEntry//-/ }"
firstEntry="${firstEntry//:/ }"
lastEntry="${lastEntry//:/ }"

# remove the following lines in production
echo "$lastEntry"
echo "$firstEntry"

# compute days in last entry
for i in `seq 1 $(echo $lastEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime+31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime+30))
    ;;
   2 )
    dTime=$(($dTime+28))
    ;;
  esac
} done

# do leap year calculations for all years between first and last entry
for i in `seq $(echo $firstEntry|awk '{print $1}') $(echo $lastEntry|awk '{print $1}')`; do {
  if [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -eq 0 ] && [ $(($i%400)) -eq 0 ]; then {
    if [ "$i" = "$(echo $firstEntry|awk '{print $1}')" ] && [ $(echo $firstEntry|awk '{print $2}') -lt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $firstEntry|awk '{print $2}') -eq 2 ] && [ $(echo $firstEntry|awk '{print $3}') -lt 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } elif [ $(($i%4)) -eq 0 ] && [ $(($i%100)) -ne 0 ]; then {
    if [ "$i" = "$(echo $lastEntry|awk '{print $1}')" ] && [ $(echo $lastEntry|awk '{print $2}') -gt 2 ]; then {
      dTime=$(($dTime+1))
    } elif [ $(echo $lastEntry|awk '{print $2}') -eq 2 ] && [ $(echo $lastEntry|awk '{print $3}') -ne 29 ]; then {
      dTime=$(($dTime+1))
    } fi
  } fi
} done

# substract days in first entry
for i in `seq 1 $(echo $firstEntry|awk '{print $2}')`; do {
  case "$i" in
   1|3|5|7|8|10|12 )
    dTime=$(($dTime-31))
    ;;
   4|6|9|11 )
    dTime=$(($dTime-30))
    ;;
   2 )
    dTime=$(($dTime-28))
    ;;
  esac
} done

dTime=$(($dTime+$(echo $lastEntry|awk '{print $3}')-$(echo $firstEntry|awk '{print $3}')))

# The above gives number of days for sample. Now we need hours, minutes, and seconds
# As a bit of hackery I just put the stuff in the best order for use in a for loop
dTime="$(($(echo $lastEntry|awk '{print $6}')-$(echo $firstEntry|awk '{print $6}'))) $(($(echo $lastEntry|awk '{print $5}')-$(echo $firstEntry|awk '{print $5}'))) $(($(echo $lastEntry|awk '{print $4}')-$(echo $firstEntry|awk '{print $4}'))) $dTime"
tmp=1
for i in $dTime; do {
  if [ $i -lt 0 ]; then {
    case "$tmp" in
     1 )
      tmp="$(($(echo $dTime|awk '{print $1}')+60)) $(($(echo $dTime|awk '{print $2}')-1))"
      dTime="$tmp $(echo $dTime|awk '{print $3" "$4}')"
      tmp=1
      ;;
     2 )
      tmp="$(($(echo $dTime|awk '{print $2}')+60)) $(($(echo $dTime|awk '{print $3}')-1))"
      dTime="$(echo $dTime|awk '{print $1}') $tmp $(echo $dTime|awk '{print $4}')"
      tmp=2
      ;;
     3 )
      tmp="$(($(echo $dTime|awk '{print $3}')+24)) $(($(echo $dTime|awk '{print $4}')-1))"
      dTime="$(echo $dTime|awk '{print $1" "$2}') $tmp"
      tmp=3
      ;;
    esac
  } fi
  tmp=$(($tmp+1))
} done

echo "The sample time is $(echo $dTime|awk '{print $4}') days, $(echo $dTime|awk '{print $3}') hours, $(echo $dTime|awk '{print $2}') minutes, and $(echo $dTime|awk '{print $1}') seconds."

คุณจะได้รับผลลัพธ์ดังนี้

2012 10 16 01 56 37
2014 09 17 18 24 02
The sample time is 700 days, 16 hours, 27 minutes, and 25 seconds.

ฉันปรับเปลี่ยนสคริปต์เล็กน้อยเพื่อให้เป็นแบบสแตนด์อโลน (เช่นเพียงแค่ตั้งค่าตัวแปร) แต่บางทีแนวคิดทั่วไปก็เข้ามาเช่นกัน คุณอาจต้องการตรวจสอบข้อผิดพลาดเพิ่มเติมสำหรับค่าลบ


Gee ทำงานเยอะมาก !! โปรดดูคำตอบของฉัน: stackoverflow.com/a/24996647/2350426

0

ฉันไม่สามารถแสดงความคิดเห็นกับคำตอบของ mcaleaa ดังนั้นฉันโพสต์ที่นี่ ตัวแปร "diff" ควรเป็นตัวเล็ก นี่คือตัวอย่าง

[root@test ~]# date1=$(date +"%s"); date
Wed Feb 21 23:00:20 MYT 2018
[root@test ~]# 
[root@test ~]# date2=$(date +"%s"); date
Wed Feb 21 23:00:33 MYT 2018
[root@test ~]# 
[root@test ~]# diff=$(($date2-$date1))
[root@test ~]# 

ตัวแปรก่อนหน้านี้ถูกประกาศในตัวพิมพ์เล็ก นี่คือสิ่งที่เกิดขึ้นเมื่อใช้ตัวพิมพ์ใหญ่

[root@test ~]# echo "Duration: $(($DIFF / 3600 )) hours $((($DIFF % 3600) / 60)) minutes $(($DIFF % 60)) seconds"
-bash: / 3600 : syntax error: operand expected (error token is "/ 3600 ")
[root@test ~]# 

ดังนั้นการแก้ไขด่วนจะเป็นเช่นนี้

[root@test ~]# echo "Duration: $(($diff / 3600 )) hours $((($diff % 3600) / 60)) minutes $(($diff % 60)) seconds"
Duration: 0 hours 0 minutes 13 seconds
[root@test ~]# 

-1

นี่คือการทุบตีของฉัน (กับบิตนำมาจากอื่น ๆ ;-)

function countTimeDiff() {
    timeA=$1 # 09:59:35
    timeB=$2 # 17:32:55

    # feeding variables by using read and splitting with IFS
    IFS=: read ah am as <<< "$timeA"
    IFS=: read bh bm bs <<< "$timeB"

    # Convert hours to minutes.
    # The 10# is there to avoid errors with leading zeros
    # by telling bash that we use base 10
    secondsA=$((10#$ah*60*60 + 10#$am*60 + 10#$as))
    secondsB=$((10#$bh*60*60 + 10#$bm*60 + 10#$bs))
    DIFF_SEC=$((secondsB - secondsA))
    echo "The difference is $DIFF_SEC seconds.";

    SEC=$(($DIFF_SEC%60))
    MIN=$((($DIFF_SEC-$SEC)%3600/60))
    HRS=$((($DIFF_SEC-$MIN*60)/3600))
    TIME_DIFF="$HRS:$MIN:$SEC";
    echo $TIME_DIFF;
}

$ countTimeDiff 2:15:55 2:55:16
The difference is 2361 seconds.
0:39:21

ไม่ผ่านการทดสอบอาจเป็นบั๊กซี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.