Skrip Bash untuk menghitung waktu yang telah berlalu

118

Saya menulis skrip di bash untuk menghitung waktu yang telah berlalu untuk eksekusi perintah saya, pertimbangkan:

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

Saya kira logika saya benar tetapi saya berakhir dengan cetakan berikut:

"Perlu beberapa detik untuk menyelesaikan tugas ini ..."

Ada yang salah dengan evaluasi string saya?

Saya percaya variabel bash tidak diketik, saya akan senang jika ada metode "string ke integer" di bash.

Michael Mao
sumber

Jawaban:

83

Salah satu $(())atau $[]akan bekerja untuk menghitung hasil operasi aritmatika. Anda menggunakan $()yang hanya mengambil string dan mengevaluasinya sebagai perintah. Ini sedikit perbedaan yang halus. Semoga ini membantu.

Seperti yang ditunjukkan tink dalam komentar pada jawaban ini, $[]sudah usang, dan $(())harus disukai.

OmnipotentEntity
sumber
7
Anda mungkin ingin menukar keduanya, karena halaman manual bash 4.x menyatakan bahwa $ [] sudah usang dan akan dihapus di versi mendatang.
tink
2
Terima kasih, saya tidak menyadarinya.
OmnipotentEntity
157

Saya merasa sangat bersih menggunakan variabel internal "$ SECONDS"

SECONDS=0 ; sleep 10 ; echo $SECONDS

Lon Kaut
sumber
10
Hanya sukses =)
Lon Kaut
1
Butuh kesuksesan, gunakan milik Anda
Gromish
3
$SECONDSmemang berfungsi untuk / bin / bash. Ini tidak berfungsi untuk / bin / dasbor, shell default di Debian dan Ubuntu.
Cameron Taggart
2
Kelemahan dari solusi ini adalah ia hanya mengukur seluruh detik, yaitu tidak dapat digunakan jika Anda membutuhkan ketepatan sub-detik.
Czechnology
@Ceknologi ya, jika Anda menggunakan sleep 0.5di atas, hasilnya kadang-kadang 0, kadang-kadang 1 (setidaknya oleh Bash 5.0.3).
jarno
52

Anda mencoba untuk mengeksekusi nomor di ENDTIMEsebagai perintah. Anda juga akan melihat kesalahan seperti 1370306857: command not found. Alih-alih gunakan ekspansi aritmatika :

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

Anda juga dapat menyimpan perintah dalam skrip terpisah commands.sh, dan menggunakan perintah waktu:

time commands.sh
nyata
sumber
28

Anda dapat menggunakan timekata kunci Bash di sini dengan string format yang sesuai

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

Inilah yang dikatakan referensi tentangTIMEFORMAT :

Nilai parameter ini digunakan sebagai string format yang menentukan bagaimana informasi pengaturan waktu untuk pipeline yang diawali dengan time kata yang dipesan harus ditampilkan. Karakter ' %' memperkenalkan urutan pelolosan yang diperluas ke nilai waktu atau informasi lainnya. Urutan pelarian dan artinya adalah sebagai berikut; kawat gigi menunjukkan bagian opsional.

%%

    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 opsional adalah digit yang menentukan presisi, jumlah digit pecahan setelah titik desimal. Nilai 0 tidak menyebabkan titik desimal atau pecahan menjadi keluaran. Paling banyak tiga tempat setelah koma desimal dapat ditentukan; nilai p lebih besar dari 3 diubah menjadi 3. Jika p tidak ditentukan, nilai 3 digunakan.

Opsional lmenentukan format yang lebih panjang, termasuk menit, dari bentuk MMmSS.FFs. Nilai p menentukan apakah pecahan dimasukkan atau tidak.

Jika variabel ini tidak disetel, Bash bertindak seolah-olah memiliki nilai

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

Jika nilainya nol, tidak ada informasi waktu yang ditampilkan. Baris baru trailing ditambahkan saat string format ditampilkan.

gniourf_gniourf
sumber
10

Untuk nomor yang lebih besar, kami mungkin ingin mencetak dalam format yang lebih mudah dibaca. Contoh di bawah ini sama seperti yang lain, tetapi juga dicetak dalam format "manusia":

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

Pengujian sederhana:

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

Keluaran:

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.

Untuk digunakan dalam skrip seperti yang dijelaskan di posting lain (tangkap titik awal kemudian panggil fungsi dengan waktu selesai:

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

Coba kode berikut:

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

Ini adalah alternatif satu baris untuk fungsi Mike Q:

secs_to_human() {
    echo "$(( ${1} / 3600 ))h $(( (${1} / 60) % 60 ))m $(( ${1} % 60 ))s"
}
Kesalahan server dari dalam
sumber
Bagus! Saya biasanya sangat bertele-tele dengan kode bash saya, ini keren.
Mike Q
Menggabungkan ini dengan SECONDSdari jawaban Lon Kaut dan mengingat bahwa $ / $ {} tidak diperlukan pada variabel aritmatika membuat kode menjadi sangat pendek bahkan dapat digunakan secara inline:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc
2

coba gunakan waktu dengan opsi detik yang telah berlalu:

/usr/bin/time -f%e sleep 1 di bawah pesta.

atau \time -f%e sleep 1dalam pesta interaktif.

lihat halaman manual waktu:

Pengguna bash shell perlu menggunakan jalur eksplisit untuk menjalankan perintah waktu eksternal dan bukan varian bawaan shell. Pada sistem di mana waktu diinstal di / usr / bin, contoh pertama akan menjadi / usr / bin / waktu wc / etc / hosts

dan

FORMATTING THE OUTPUT
...
    %      A literal '%'.
    e      Elapsed  real  (wall  clock) time used by the process, in
                 seconds.
Lynch
sumber
1
/bin/timetidak akan berfungsi di sini: OP menyebutkan satu blok . Jadi kami sangat membutuhkan kata kunci di timesini.
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
Rafał Białas
sumber
-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
Rafał Białas
sumber