it-swarm-vi.com

Làm thế nào để đo thời gian thực hiện chương trình và lưu trữ bên trong một biến

Để tìm hiểu các hoạt động nhất định trong tập lệnh Bash (v4 +) mất bao lâu, tôi muốn phân tích đầu ra từ lệnh time "riêng biệt" và (cuối cùng) nắm bắt nó trong một biến Bash (let VARNAME=...).

Bây giờ, tôi đang sử dụng time -f '%e' ... (hay đúng hơn command time -f '%e' ... vì có sẵn Bash), nhưng vì tôi đã chuyển hướng đầu ra của lệnh đã thực thi nên tôi thực sự bị mất về cách tôi sẽ bắt được đầu ra của lệnh time. Về cơ bản vấn đề ở đây là để tách đầu ra of time khỏi đầu ra của (các) lệnh đã thực hiện.

Điều tôi muốn là chức năng trong việc đếm lượng thời gian tính bằng giây (số nguyên) giữa việc bắt đầu một lệnh và hoàn thành. Nó không phải là lệnh time hoặc tích hợp sẵn tương ứng.


Chỉnh sửa : đưa ra hai câu trả lời hữu ích dưới đây, tôi muốn thêm hai làm rõ.

  1. Tôi không muốn vứt bỏ đầu ra của lệnh đã thực thi, nhưng nó thực sự không quan trọng cho dù nó kết thúc trên thiết bị xuất chuẩn hay thiết bị xuất chuẩn.
  2. Tôi muốn một cách tiếp cận trực tiếp hơn một cách gián tiếp (nghĩa là bắt đầu ra trực tiếp thay vì lưu trữ nó trong các tệp trung gian).

Giải pháp sử dụng date cho đến nay đóng lại với những gì tôi muốn.

65
0xC0000022L

Để nhận đầu ra của time vào một var, hãy sử dụng như sau:

[email protected] $ mytime="$(time ( ls ) 2>&1 1>/dev/null )"
[email protected] $ echo "$mytime"

real    0m0.006s
user    0m0.001s
sys     0m0.005s

Bạn cũng có thể chỉ yêu cầu một loại thời gian duy nhất, ví dụ: không tưởng

[email protected] $ utime="$( TIMEFORMAT='%lU';time ( ls ) 2>&1 1>/dev/null )"
[email protected] $ echo "$utime"
0m0.000s

Để có thời gian bạn cũng có thể sử dụng date +%s.%N, vì vậy hãy lấy nó trước và sau khi thực hiện và tính toán diff:

START=$(date +%s.%N)
command
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF
89
binfalse

Trong bash, đầu ra của cấu trúc time đi đến lỗi tiêu chuẩn của nó và bạn có thể chuyển hướng lỗi tiêu chuẩn của đường ống mà nó ảnh hưởng. Vì vậy, hãy bắt đầu với một lệnh ghi vào luồng đầu ra và lỗi: sh -c 'echo out; echo 1>&2 err'. Để không trộn lẫn luồng lỗi của lệnh với đầu ra từ time, chúng ta có thể tạm thời chuyển luồng lỗi của lệnh sang một bộ mô tả tệp khác:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; }

Điều này ghi out vào fd 1, err vào fd 3 và thời gian cho fd 2:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } \
    3> >(sed 's/^/ERR:/') 2> >(sed 's/^/TIME:/') > >(sed 's/^/OUT:/')

Sẽ dễ chịu hơn khi có err trên fd 2 và thời gian trên fd 3, vì vậy chúng tôi trao đổi chúng, điều này rất khó khăn vì không có cách nào để trao đổi trực tiếp hai mô tả tệp:

{ { { time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } 3>&2 2>&4; } 4>&3; } 3> >(sed 's/^/TIME:/') 2> >(sed 's/^/ERR:/') > >(sed 's/^/OUT:/')

Điều này cho thấy cách bạn có thể xử lý hậu quả đầu ra của lệnh, nhưng nếu bạn muốn nắm bắt cả đầu ra của lệnh và thời gian của lệnh, bạn cần phải làm việc chăm chỉ hơn. Sử dụng một tập tin tạm thời là một giải pháp. Trong thực tế, đó là giải pháp đáng tin cậy duy nhất nếu bạn cần nắm bắt cả lỗi tiêu chuẩn của lệnh và đầu ra tiêu chuẩn của nó. Nhưng nếu không, bạn có thể nắm bắt toàn bộ đầu ra và tận dụng thực tế là time có định dạng có thể dự đoán được (nếu bạn sử dụng time -p để lấy định dạng POSIX hoặc biến TIMEFORMAT cụ thể của bash).

nl=$'\n'
output=$(TIMEFORMAT='%R %U %S %P'; mycommand)
set ${output##*$nl}; real_time=$1 user_time=$2 system_time=$3 cpu_percent=$4
output=${output%$nl*}

Nếu bạn chỉ quan tâm đến thời gian đồng hồ treo tường, chạy date trước và sau là một giải pháp đơn giản (nếu không chính xác hơn một chút do mất thêm thời gian để tải lệnh bên ngoài).

Nếu bạn đang ở bash (chứ không phải sh) và bạn KHÔNG cần độ chính xác của giây phụ, bạn có thể bỏ qua cuộc gọi đến date và thực hiện mà không cần sinh thêm các quy trình, mà không phải tách đầu ra kết hợp và không phải bắt và phân tích đầu ra từ bất kỳ lệnh nào:

# SECONDS is a bash special variable that returns the seconds since set.
SECONDS=0
mycmd <infile >outfile 2>errfile
DURATION_IN_SECONDS=$SECONDS
# Now `$DURATION_IN_SECONDS` is the number of seconds.
8
sanpath

Theo thời gian, đầu ra lệnh xuất hiện trên thiết bị xuất chuẩn và thời gian xuất hiện trên thiết bị xuất chuẩn. Vì vậy, để tách chúng, bạn có thể làm:

command time -f '%e' [command] 1>[command output file] 2>[time output file]

Nhưng, bây giờ thời gian là trong một tập tin. Tôi không nghĩ Bash có thể đặt stderr vào một biến trực tiếp. Nếu bạn không ngại chuyển hướng đầu ra của lệnh ở đâu đó, bạn có thể làm:

FOO=$((( command time -f '%e' [command]; ) 1>outputfile; ) 2>&1; )

Khi bạn thực hiện việc này, đầu ra của lệnh sẽ ở outputfile và thời gian cần để chạy sẽ ở $FOO.

7
marinus

Trong việc kết hợp tất cả các phản hồi trước đó, khi ở OSX

ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G31+

bạn có thể làm như

microtime() {
    python -c 'import time; print time.time()'
}
compute() {
    local START=$(microtime)
    #$1 is command $2 are args
    local END=$(microtime)
    DIFF=$(echo "$END - $START" | bc)
    echo "$1\t$2\t$DIFF"
}
4
loretoparisi

Với mục đích đó, có lẽ tốt hơn là sử dụng times (hơn time) trong bash in thời gian tích lũy của người dùng và hệ thống cho Shell và cho các quy trình chạy từ Shell, ví dụ sử dụng:

$ (sleep 2; times) | (read tuser tsys; echo $tuser:$tsys)
0m0.001s:0m0.003s

Xem: help -m times để biết thêm.

4
kenorb

Tải về /bin/time (ví dụ. pacman -S time)

Vì vậy, thay vì lỗi khi thử -f cờ:

$ time -f %e sleep 0.5
bash: -f: command not found

real    0m0.001s
user    0m0.001s
sys     0m0.001s

Bạn thực sự có thể sử dụng nó:

$ /bin/time -f %e sleep 0.5
0.50

Và nhận được những gì bạn muốn - thời gian trong biến (ví dụ sử dụng %e trong thời gian thực đã trôi qua, đối với các tùy chọn khác, hãy kiểm tra man time):

#!/bin/bash
tmpf="$(mktemp)"
/bin/time -f %e -o "$tmpf" sleep 0.5
variable="$(cat "$tmpf")"
rm "$tmpf"

echo "$variable"
1
Grzegorz Wierzowiecki

Giống như ngẩng cao đầu khi làm việc với các tuyên bố trên và đặc biệt là ghi nhớ câu trả lời của Grzegorz. Tôi đã rất ngạc nhiên khi thấy trên hệ thống Xenial Ubuntu 16.04 của mình hai kết quả này:

$ which time
/usr/bin/time

$ time -f %e sleep 4
-f: command not found
real    0m0.071s
user    0m0.056s
sys     0m0.012s

$ /usr/bin/time -f %e sleep 4
4.00

Tôi không có bất kỳ bí danh nào được đặt và vì vậy tôi không biết tại sao điều này lại xảy ra.

1
Paul Pritchard

hãy thử điều này, nó chạy một lệnh đơn giản với các đối số và đặt thời gian $ real $ user $ sys và giữ nguyên mã thoát.

Nó cũng không rẽ nhánh con hoặc chà đạp lên bất kỳ biến nào ngoại trừ sys người dùng thực và không can thiệp vào việc chạy tập lệnh

timer () {
  { time { "[email protected]" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $?
  read -d "" _ real _ user _ sys _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1
}

ví dụ.

  timer find /bin /sbin /usr rm /tmp/
  echo $real $user $sys

lưu ý: nó chỉ nhân với lệnh đơn giản không phải là một đường ống, mà các thành phần của nó được chạy trong một lớp con

Phiên bản này cho phép bạn chỉ định $ 1 tên của các biến sẽ nhận được 3 lần:

timer () {
  { time { "${@:4}" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $? "[email protected]"
  read -d "" _ "$2" _ "$3" _ "$4" _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1
}

ví dụ.

  timer r u s find /bin /sbin /usr rm /tmp/
  echo $r $u $s

và có thể hữu ích nếu cuối cùng nó được gọi là đệ quy, để tránh chà đạp lên thời gian; nhưng sau đó r u s vv nên được khai báo cục bộ trong sử dụng của họ.

http://blog.sam.liddicott.com/2016/01/timeing-bash-commands.html

0
Sam Liddicott