Tập lệnh Bash để tính toán thời gian đã trôi qua


118

Tôi đang viết một tập lệnh trong bash để tính toán thời gian trôi qua để thực hiện các lệnh của mình, hãy xem xét:

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

Tôi đoán logic của tôi là đúng tuy nhiên tôi kết thúc với bản in sau:

"Phải mất vài giây để hoàn thành nhiệm vụ này ..."

Có gì sai với đánh giá chuỗi của tôi không?

Tôi tin rằng các biến bash là không định kiểu, tôi rất thích nếu có một phương thức "chuỗi thành số nguyên" trong bash.

Câu trả lời:


83

Hoặc $(())hoặc $[]sẽ làm việc để tính kết quả của một hoạt động số học. Bạn đang sử dụng $()mà chỉ đơn giản là lấy chuỗi và đánh giá nó như một lệnh. Đó là một chút khác biệt tinh tế. Hi vọng điêu nay co ich.

Như tink đã chỉ ra trong các nhận xét về câu trả lời này, $[]không được chấp nhận và $(())nên được ưu tiên.


7
Bạn có thể muốn hoán đổi hai thứ đó, vì trang man-bash 4.x tuyên bố rằng $ [] không được dùng nữa và sẽ bị xóa trong các phiên bản sau.
tink

2
Cảm ơn bạn, tôi đã không biết.
OmnipotentEntity

157

Tôi thấy việc sử dụng biến nội bộ "$ SECONDS" rất hữu ích

SECONDS=0 ; sleep 10 ; echo $SECONDS


10
Chỉ có thành công =)
Lon Kaut

1
Cần thành công, hãy sử dụng của bạn
Gromish

3
$SECONDSthực sự hoạt động cho / bin / bash. Nó không hoạt động đối với / bin / dash, trình bao mặc định trong Debian và Ubuntu.
Cameron Taggart

2
Nhược điểm của giải pháp này là nó chỉ đo được toàn bộ giây, tức là không thể sử dụng được nếu bạn cần độ chính xác dưới giây.
Czechnology

@Czechnology vâng, nếu bạn sử dụng sleep 0.5ở trên, kết quả đôi khi là 0, đôi khi là 1 (ít nhất bằng Bash 5.0.3).
jarno

52

Bạn đang cố gắng thực thi số trong lệnh ENDTIMEdưới dạng lệnh. Bạn cũng sẽ thấy một lỗi như 1370306857: command not found. Thay vào đó, hãy sử dụng khai triển số học :

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

Bạn cũng có thể lưu các lệnh trong một tập lệnh riêng biệt commands.shvà sử dụng lệnh thời gian:

time commands.sh

28

Bạn có thể sử dụng timetừ khóa của Bash tại đây với chuỗi định dạng thích hợp

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

Đây là những gì tham chiếu nói vềTIMEFORMAT :

Giá trị của tham số này được sử dụng như một chuỗi định dạng chỉ định cách time hiển thị thông tin thời gian cho các đường ống có tiền tố là từ dành riêng. Ký %tự '' giới thiệu một chuỗi thoát được mở rộng thành giá trị thời gian hoặc thông tin khác. Các trình tự thoát và ý nghĩa của chúng như sau; dấu ngoặc nhọn biểu thị các phần tùy chọn.

%%

    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 tùy chọn là một chữ số xác định độ chính xác, số chữ số phân số sau dấu thập phân. Giá trị 0 không xuất ra dấu chấm thập phân hoặc phân số. Có thể chỉ định nhiều nhất ba vị trí sau dấu thập phân; giá trị của p lớn hơn 3 được thay đổi thành 3. Nếu p không được chỉ định, giá trị 3 được sử dụng.

Tùy chọn lchỉ định một định dạng dài hơn, bao gồm cả phút, của dạng MMmSS.FFs. Giá trị của p xác định phân số có được đưa vào hay không.

Nếu biến này không được đặt, Bash sẽ hoạt động như thể nó có giá trị

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

Nếu giá trị là null, không có thông tin thời gian nào được hiển thị. Một dòng mới ở cuối được thêm vào khi chuỗi định dạng được hiển thị.


10

Đối với số lượng lớn hơn, chúng tôi có thể muốn in ở định dạng dễ đọc hơn. Ví dụ dưới đây thực hiện tương tự như các ví dụ khác nhưng cũng in ở định dạng "người":

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

Kiểm tra đơn giản:

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

Đầu ra:

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.

Để sử dụng trong một tập lệnh như được mô tả trong các bài viết khác (nắm bắt điểm bắt đầu sau đó gọi hàm với thời gian kết thúc:

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

9

Hãy thử mã sau:

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

5

Đây là một giải pháp thay thế một lớp lót cho chức năng của Mike Q:

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

Đẹp! Tôi thường rất dài dòng với mã cơ bản của mình, điều này thật tuyệt.
Mike Q

Kết hợp điều này với câu trả lờiSECONDS của Lon Kaut và lưu ý rằng $ / $ {} là không cần thiết đối với các biến số học khiến mã ngắn đến mức thậm chí có thể được sử dụng nội tuyến:echo "$((SECONDS/3600))h $(((SECONDS/60)%60))m $((SECONDS%60))s"
ssc

2

thử sử dụng thời gian với tùy chọn giây đã trôi qua:

/usr/bin/time -f%e sleep 1 dưới bash.

hoặc \time -f%e sleep 1trong bash tương tác.

xem trang người đàn ông thời gian:

Người dùng của bash shell cần sử dụng một đường dẫn rõ ràng để chạy lệnh thời gian bên ngoài chứ không phải biến thể nội trang của shell. Trên hệ thống có cài đặt thời gian trong / usr / bin, ví dụ đầu tiên sẽ trở thành / 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/timesẽ không hoạt động ở đây: OP đề cập đến một khối . Vì vậy, chúng tôi thực sự cần từ khóa timeở đây.
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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.