Geçen zamanı hesaplamak için Bash betiği


119

Komutlarımın çalıştırılması için geçen süreyi hesaplamak için bash'de bir komut dosyası yazıyorum, göz önünde bulundurun:

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

Sanırım mantığım doğru ancak şu çıktıyı alıyorum:

"Bu görevi tamamlamak saniyeler sürer ..."

Dizi değerlendirmemde bir sorun var mı?

Bash değişkenlerinin türsüz olduğuna inanıyorum, yine de bash içinde bir "tamsayıya dizge" yöntemi olsa çok sevinirim.

Yanıtlar:


84

Ya $(())veya $[]aritmetik bir işlemin sonucunu hesaplanması için çalışacaktır. Siz $()hangisini basitçe dizeyi alıp bir komut olarak değerlendiriyorsunuz. Bu biraz ince bir ayrım. Bu yardımcı olur umarım.

Tink'in bu cevapla ilgili yorumlarda belirttiği gibi $[], kullanımdan kaldırılmıştır ve $(())tercih edilmelidir.


7
Bash 4.x man-page $ [] ' ın kullanımdan kaldırıldığını ve gelecekteki sürümlerde kaldırılacağını belirttiğinden, bu ikisini değiştirmek isteyebilirsiniz .
tink

2
Teşekkür ederim, farkında değildim.
OmnipotentEntity

157

"$ SECONDS" dahili değişkenini kullanmayı çok temiz buluyorum

SECONDS=0 ; sleep 10 ; echo $SECONDS


10
Tek başarı =)
Lon Kaut

1
Başarıya mı ihtiyacınız var, sizinkini kullanın
Gromish

3
$SECONDSgerçekten / bin / bash için çalışıyor. Debian ve Ubuntu'daki varsayılan kabuk olan / bin / dash için çalışmaz.
Cameron Taggart

2
Bu çözümün dezavantajı, yalnızca tam saniyeleri ölçmesidir, yani saniyenin altında bir hassasiyete ihtiyacınız varsa kullanılamaz.
Czechnology

@Czechnology evet, sleep 0.5yukarıda kullanırsanız , sonuç bazen 0, bazen 1 (en azından Bash 5.0.3'e göre).
jarno

52

ENDTIMEBir komut olarak içindeki sayıyı çalıştırmaya çalışıyorsunuz . Ayrıca gibi bir hata görmelisiniz 1370306857: command not found. Bunun yerine aritmetik genişletmeyi kullanın :

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

Ayrıca komutları ayrı bir komut dosyasında kaydedebilir commands.shve time komutunu kullanabilirsiniz:

time commands.sh

28

Bash'in timeanahtar kelimesini burada uygun bir biçim dizesiyle kullanabilirsiniz

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

İşte ne referans hakkında söylediğiTIMEFORMAT :

Bu parametrenin değeri, önek olarak time ayrılmış sözcüğün bulunduğu ardışık düzenler için zamanlama bilgilerinin nasıl görüntülenmesi gerektiğini belirten bir biçim dizisi olarak kullanılır . ' %' Karakteri, bir zaman değerine veya diğer bilgilere genişletilen bir kaçış dizisi sunar. Kaçış dizileri ve anlamları aşağıdaki gibidir; kaşlı ayraçlar, isteğe bağlı bölümleri belirtir.

%%

    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. 

İsteğe bağlı p , kesinliği, ondalık noktadan sonraki kesirli basamakların sayısını belirten bir basamaktır. 0 değeri, ondalık basamağın veya kesirin çıkılmasına neden olmaz. Ondalık noktadan sonra en fazla üç basamak belirtilebilir; p'nin 3'ten büyük değerleri 3'e değiştirilir. p belirtilmezse, 3 değeri kullanılır.

İsteğe bağlı l, MMmSS.FFs biçiminde dakikalar dahil daha uzun bir biçim belirtir. P'nin değeri , kesrin dahil edilip edilmeyeceğini belirler.

Bu değişken ayarlanmazsa, Bash değere sahipmiş gibi davranır

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

Değer boş ise, zamanlama bilgisi görüntülenmez. Biçim dizesi görüntülendiğinde sondaki satırsonu eklenir.


10

Daha büyük sayılar için daha okunaklı bir biçimde yazdırmak isteyebiliriz. Aşağıdaki örnek diğerleriyle aynıdır ancak "insan" biçiminde de yazdırılır:

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."
}

Basit test:

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

Çıktı:

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.

Diğer gönderilerde açıklandığı gibi bir komut dosyasında kullanmak için (başlangıç ​​noktasını yakalayın ve ardından bitiş zamanıyla işlevi çağırın:

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

9

Aşağıdaki kodu deneyin:

start=$(date +'%s') && sleep 5 && echo "It took $(($(date +'%s') - $start)) seconds"

5

Bu, Mike Q'nun işlevine tek satırlık bir alternatiftir:

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

Güzel! Genelde bash kodumla çok ayrıntılı olurum, bu harika.
Mike Q

Bu özellikleri SECONDSboylam Kaut en dan cevap ve zihin tutulması $ / $ {} aritmetik değişkenler üzerinde gereksizdir : yapar kod kadar kısa bile inline kullanılabilirecho "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
SSC

2

geçen saniye seçeneğiyle zamanı kullanmayı deneyin:

/usr/bin/time -f%e sleep 1 bash altında.

veya \time -f%e sleep 1etkileşimli bash.

time man sayfasına bakın:

Bash kabuğunun kullanıcılarının, dış zaman komutunu çalıştırmak için kabuk yerleşik değişkenini değil, açık bir yol kullanması gerekir. Zamanın / usr / bin içine yüklendiği sistemde ilk örnek / usr / bin / time wc / etc / hosts olacaktır.

ve

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

1
/bin/timeburada çalışmayacak: OP bir bloktan bahseder . Bu yüzden gerçekten timeburada anahtar kelimeye ihtiyacımız var.
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
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.