Bash script เพื่อคำนวณเวลาที่ผ่านไป


119

ฉันกำลังเขียนสคริปต์ใน bash เพื่อคำนวณเวลาที่ผ่านไปสำหรับการดำเนินการตามคำสั่งของฉันให้พิจารณา:

STARTTIME=$(date +%s)
#command block that takes time to complete...
#........
ENDTIME=$(date +%s)
echo "It takes $($ENDTIME - $STARTTIME) seconds to complete this task..."

ฉันเดาว่าตรรกะของฉันถูกต้อง แต่ฉันก็พิมพ์ออกมาต่อไปนี้:

"ใช้เวลาไม่กี่วินาทีในการทำงานนี้ให้เสร็จ ... "

มีอะไรผิดปกติกับการประเมินสตริงของฉัน?

ฉันเชื่อว่าตัวแปร bash ไม่ได้ถูกพิมพ์ฉันจะชอบถ้ามีวิธี "string to integer" ใน bash อย่างไรก็ตาม

คำตอบ:


84

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

ดังที่ได้ระบุไว้ในความคิดเห็นเกี่ยวกับคำตอบ$[]นี้เลิกใช้แล้วและ$(())ควรได้รับการสนับสนุน


7
คุณอาจต้องการสลับสองอย่างนี้เนื่องจาก bash 4.x man-page ระบุว่า$ []เลิกใช้งานแล้วและจะถูกลบออกในเวอร์ชันต่อ ๆ ไป
tink

2
ขอบคุณฉันไม่รู้ตัว
OmnipotentEntity

157

ฉันพบว่าการใช้ตัวแปรภายใน "$ SECONDS" เป็นไปอย่างถูกต้อง

SECONDS=0 ; sleep 10 ; echo $SECONDS


10
ประสบความสำเร็จเท่านั้น =)
Lon Kaut

1
ต้องการความสำเร็จใช้ของคุณ
Gromish

3
$SECONDSใช้งานได้จริงสำหรับ / bin / bash มันใช้ไม่ได้กับ / bin / dash ซึ่งเป็นเชลล์เริ่มต้นใน Debian และ Ubuntu
Cameron Taggart

2
ข้อเสียของการแก้ปัญหานี้คือการวัดเพียงเสี้ยววินาทีเท่านั้นกล่าวคือใช้ไม่ได้หากคุณต้องการความแม่นยำย่อยวินาที
Czechnology

@Czechnology ใช่ถ้าคุณใช้sleep 0.5ในข้างต้นผลลัพธ์จะเป็น 0 บางครั้งบางครั้ง 1 (อย่างน้อยก็คือ Bash 5.0.3)
jarno

52

คุณกำลังพยายามเรียกใช้หมายเลขในENDTIMEas a command คุณควรเห็นข้อผิดพลาดเช่น1370306857: command not found. ใช้การขยายเลขคณิตแทน:

echo "It takes $(($ENDTIME - $STARTTIME)) seconds to complete this task..."

คุณยังสามารถบันทึกคำสั่งในสคริปต์แยกต่างหากcommands.shและใช้คำสั่ง time:

time commands.sh

28

คุณสามารถใช้timeคีย์เวิร์ดของ Bash ได้ที่นี่ด้วยสตริงรูปแบบที่เหมาะสม

TIMEFORMAT='It takes %R seconds to complete this task...'
time {
    #command block that takes time to complete...
    #........
 }

นี่คือสิ่งที่มีการอ้างอิงกล่าวเกี่ยวกับTIMEFORMAT :

ค่าของพารามิเตอร์นี้ใช้เป็นสตริงรูปแบบที่ระบุว่าtime ควรแสดงข้อมูลเวลาสำหรับไปป์ไลน์ที่นำหน้าด้วยคำสงวนอย่างไร ว่า ' %' ตัวละครแนะนำลำดับหนีที่มีการขยายไปยังค่าเวลาหรือข้อมูลอื่น ๆ ลำดับการหลบหนีและความหมายมีดังนี้ วงเล็บปีกกาแสดงถึงส่วนเสริม

%%

    A literal ‘%’.
%[p][l]R

    The elapsed time in seconds.
%[p][l]U

    The number of CPU seconds spent in user mode.
%[p][l]S

    The number of CPU seconds spent in system mode.
%P

    The CPU percentage, computed as (%U + %S) / %R. 

ตัวเลือกpคือตัวเลขที่ระบุความแม่นยำจำนวนหลักเศษส่วนหลังจุดทศนิยม ค่า 0 ทำให้ไม่มีการส่งออกจุดทศนิยมหรือเศษส่วน อาจระบุได้มากที่สุดสามตำแหน่งหลังจากจุดทศนิยม ค่าของp ที่มากกว่า 3 จะเปลี่ยนเป็น 3 หากไม่ระบุpจะใช้ค่า 3

ทางเลือกlระบุรูปแบบที่ยาวขึ้นรวมถึงนาทีของรูปแบบMMmSS.FFs ค่าpกำหนดว่าจะรวมเศษส่วนหรือไม่

หากไม่ได้ตั้งค่าตัวแปรนี้ Bash จะทำราวกับว่ามีค่า

$'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'

หากค่าเป็นโมฆะจะไม่มีการแสดงข้อมูลเวลา บรรทัดใหม่ต่อท้ายจะถูกเพิ่มเมื่อสตริงรูปแบบแสดงขึ้น


10

สำหรับจำนวนที่มากขึ้นเราอาจต้องการพิมพ์ในรูปแบบที่อ่านง่ายขึ้น ตัวอย่างด้านล่างนี้เหมือนกับภาพอื่น ๆ แต่ยังพิมพ์ในรูปแบบ "มนุษย์" ด้วย:

secs_to_human() {
    if [[ -z ${1} || ${1} -lt 60 ]] ;then
        min=0 ; secs="${1}"
    else
        time_mins=$(echo "scale=2; ${1}/60" | bc)
        min=$(echo ${time_mins} | cut -d'.' -f1)
        secs="0.$(echo ${time_mins} | cut -d'.' -f2)"
        secs=$(echo ${secs}*60|bc|awk '{print int($1+0.5)}')
    fi
    echo "Time Elapsed : ${min} minutes and ${secs} seconds."
}

การทดสอบอย่างง่าย:

secs_to_human "300"
secs_to_human "305"
secs_to_human "59"
secs_to_human "60"
secs_to_human "660"
secs_to_human "3000"

เอาท์พุท:

Time Elapsed : 5 minutes and 0 seconds.
Time Elapsed : 5 minutes and 5 seconds.
Time Elapsed : 0 minutes and 59 seconds.
Time Elapsed : 1 minutes and 0 seconds.
Time Elapsed : 11 minutes and 0 seconds.
Time Elapsed : 50 minutes and 0 seconds.

เพื่อใช้ในสคริปต์ตามที่อธิบายไว้ในโพสต์อื่น ๆ (จับจุดเริ่มต้นจากนั้นเรียกใช้ฟังก์ชันพร้อมเวลาสิ้นสุด:

start=$(date +%s)
# << performs some task here >>
secs_to_human "$(($(date +%s) - ${start}))"


5

นี่เป็นทางเลือกเดียวสำหรับฟังก์ชั่นของ Mike Q:

secs_to_human() {
    echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}

ดี! ฉันมักจะใช้รหัสทุบตีของฉันอย่างละเอียดมากมันเจ๋งมาก
Mike Q

เมื่อรวมสิ่งนี้เข้ากับคำตอบSECONDSของ Lon Kaut และโปรดทราบว่า$ / $ {} ไม่จำเป็นในตัวแปรเลขคณิตทำให้โค้ดสั้นมากจนสามารถใช้แบบอินไลน์ได้:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc

2

ลองใช้เวลากับตัวเลือกวินาทีที่ผ่านไป:

/usr/bin/time -f%e sleep 1 ภายใต้การทุบตี

หรือ\time -f%e sleep 1ในการทุบตีแบบโต้ตอบ

ดูหน้าคนเวลา:

ผู้ใช้ bash เชลล์จำเป็นต้องใช้เส้นทางที่ชัดเจนเพื่อรันคำสั่งเวลาภายนอกไม่ใช่ตัวแปรบิวอินของเชลล์ บนระบบที่ติดตั้งเวลาใน / usr / bin ตัวอย่างแรกจะกลายเป็น / usr / bin / time wc / etc / hosts

และ

FORMATTING THE OUTPUT
...
    %      A literal '%'.
    e      Elapsed  real  (wall  clock) time used by the process, in
                 seconds.

1
/bin/timeไม่ได้ไปทำงานที่นี่: OP กล่าวถึงบล็อก ดังนั้นเราจึงต้องการคำหลักtimeที่นี่
gniourf_gniourf

-3
start=$(date +%Y%m%d%H%M%S);
for x in {1..5};
do echo $x;
sleep 1; done;
end=$(date +%Y%m%d%H%M%S);
elapsed=$(($end-$start));
ftime=$(for((i=1;i<=$((${#end}-${#elapsed}));i++));
        do echo -n "-";
        done;
        echo ${elapsed});
echo -e "Start  : ${start}\nStop   : ${end}\nElapsed: ${ftime}"

Start  : 20171108005304
Stop   : 20171108005310
Elapsed: -------------6

-3
    #!/bin/bash

    time_elapsed(){
    appstop=$1; appstart=$2

    ss_strt=${appstart:12:2} ;ss_stop=${appstop:12:2}
    mm_strt=${appstart:10:2} ;mm_stop=${appstop:10:2}
     hh_strt=${appstart:8:2} ; hh_stop=${appstop:8:2}
     dd_strt=${appstart:6:2} ; dd_stop=${appstop:6:2}
     mh_strt=${appstart:4:2} ; mh_stop=${appstop:4:2}
     yy_strt=${appstart:0:4} ; yy_stop=${appstop:0:4}

    if [ "${ss_stop}" -lt "${ss_strt}" ]; then ss_stop=$((ss_stop+60)); mm_stop=$((mm_stop-1)); fi
    if [ "${mm_stop}" -lt "0" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${mm_stop}" -lt "${mm_strt}" ]; then mm_stop=$((mm_stop+60)); hh_stop=$((hh_stop-1)); fi
    if [ "${hh_stop}" -lt "0" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi
    if [ "${hh_stop}" -lt "${hh_strt}" ]; then hh_stop=$((hh_stop+24)); dd_stop=$((dd_stop-1)); fi

    if [ "${dd_stop}" -lt "0" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi
    if [ "${dd_stop}" -lt "${dd_strt}" ]; then dd_stop=$((dd_stop+$(mh_days $mh_stop $yy_stop))); mh_stop=$((mh_stop-1)); fi

    if [ "${mh_stop}" -lt "0" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi
    if [ "${mh_stop}" -lt "${mh_strt}" ]; then mh_stop=$((mh_stop+12)); yy_stop=$((yy_stop-1)); fi

    ss_espd=$((10#${ss_stop}-10#${ss_strt})); if [ "${#ss_espd}" -le "1" ]; then ss_espd=$(for((i=1;i<=$((${#ss_stop}-${#ss_espd}));i++)); do echo -n "0"; done; echo ${ss_espd}); fi
    mm_espd=$((10#${mm_stop}-10#${mm_strt})); if [ "${#mm_espd}" -le "1" ]; then mm_espd=$(for((i=1;i<=$((${#mm_stop}-${#mm_espd}));i++)); do echo -n "0"; done; echo ${mm_espd}); fi
    hh_espd=$((10#${hh_stop}-10#${hh_strt})); if [ "${#hh_espd}" -le "1" ]; then hh_espd=$(for((i=1;i<=$((${#hh_stop}-${#hh_espd}));i++)); do echo -n "0"; done; echo ${hh_espd}); fi
    dd_espd=$((10#${dd_stop}-10#${dd_strt})); if [ "${#dd_espd}" -le "1" ]; then dd_espd=$(for((i=1;i<=$((${#dd_stop}-${#dd_espd}));i++)); do echo -n "0"; done; echo ${dd_espd}); fi
    mh_espd=$((10#${mh_stop}-10#${mh_strt})); if [ "${#mh_espd}" -le "1" ]; then mh_espd=$(for((i=1;i<=$((${#mh_stop}-${#mh_espd}));i++)); do echo -n "0"; done; echo ${mh_espd}); fi
    yy_espd=$((10#${yy_stop}-10#${yy_strt})); if [ "${#yy_espd}" -le "1" ]; then yy_espd=$(for((i=1;i<=$((${#yy_stop}-${#yy_espd}));i++)); do echo -n "0"; done; echo ${yy_espd}); fi

    echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}"
    #return $(echo -e "${yy_espd}-${mh_espd}-${dd_espd} ${hh_espd}:${mm_espd}:${ss_espd}")
    }

    mh_days(){
    mh_stop=$1; yy_stop=$2; #also checks if it's leap year or not

    case $mh_stop in
     [1,3,5,7,8,10,12]) mh_stop=31
     ;;
     2) (( !(yy_stop % 4) && (yy_stop % 100 || !(yy_stop % 400) ) )) && mh_stop=29 || mh_stop=28
     ;;
     [4,6,9,11]) mh_stop=30
     ;;
    esac

    return ${mh_stop}
    }

    appstart=$(date +%Y%m%d%H%M%S); read -p "Wait some time, then press nay-key..." key; appstop=$(date +%Y%m%d%H%M%S); elapsed=$(time_elapsed $appstop $appstart); echo -e "Start...: ${appstart:0:4}-${appstart:4:2}-${appstart:6:2} ${appstart:8:2}:${appstart:10:2}:${appstart:12:2}\nStop....: ${appstop:0:4}-${appstop:4:2}-${appstop:6:2} ${appstop:8:2}:${appstop:10:2}:${appstop:12:2}\n$(printf '%0.1s' "="{1..30})\nElapsed.: ${elapsed}"

    exit 0


-------------------------------------------- return
Wait some time, then press nay-key...
Start...: 2017-11-09 03:22:17
Stop....: 2017-11-09 03:22:18
==============================
Elapsed.: 0000-00-00 00:00:01
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.