it-swarm-id.com

Bagaimana mengukur waktu pelaksanaan program dan menyimpannya di dalam variabel

Untuk mengetahui berapa lama operasi tertentu dalam skrip Bash (v4 +), saya ingin mengurai output dari perintah time "terpisah" dan (akhirnya) menangkapnya dalam variabel Bash (let VARNAME=...).

Sekarang, saya menggunakan time -f '%e' ... (atau sebaiknya command time -f '%e' ... karena Bash built-in), tetapi karena saya sudah mengarahkan ulang output dari perintah yang dieksekusi, saya benar-benar bingung bagaimana saya akan menangkap output dari perintah time. Pada dasarnya masalah di sini adalah ntuk memisahkan output dari time dari output dari perintah yang dieksekusi.

Yang saya inginkan adalah fungsionalitas menghitung jumlah waktu dalam detik (bilangan bulat) antara memulai perintah dan penyelesaiannya. Itu tidak harus menjadi perintah time atau masing-masing built-in.


Edit: mengingat dua jawaban yang berguna di bawah ini, saya ingin menambahkan dua klarifikasi.

  1. Saya tidak ingin membuang output dari perintah yang dieksekusi, tetapi tidak terlalu penting apakah itu berakhir pada stdout atau stderr.
  2. Saya lebih suka pendekatan langsung daripada yang tidak langsung (mis. Menangkap keluaran langsung sebagai lawan untuk menyimpannya dalam file perantara).

Solusi menggunakan date sejauh ini mendekati apa yang saya inginkan.

65
0xC0000022L

Untuk mendapatkan output time ke var gunakan yang berikut ini:

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

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

Anda juga dapat meminta satu jenis waktu saja, mis. utime:

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

Untuk mendapatkan waktu Anda juga bisa menggunakan date +%s.%N, jadi ambil sebelum dan sesudah eksekusi dan hitung diff:

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

Dalam bash, output dari konstruksi time menuju kesalahan standarnya, dan Anda bisa mengarahkan kesalahan standar dari pipeline yang dipengaruhinya. Jadi mari kita mulai dengan perintah yang menulis ke output dan error streamas: sh -c 'echo out; echo 1>&2 err'. Agar tidak mencampur aliran kesalahan perintah dengan output dari time, kita untuk sementara dapat mengalihkan aliran kesalahan perintah ke deskriptor file yang berbeda:

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

Ini menulis out ke fd 1, err ke fd 3, dan waktu ke 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:/')

Akan lebih menyenangkan untuk memiliki err pada fd 2 dan waktu pada fd 3, jadi kami menukar mereka, yang rumit karena tidak ada cara langsung untuk menukar dua deskriptor file:

{ { { 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:/')

Ini menunjukkan bagaimana Anda dapat memposting proses output dari perintah, tetapi jika Anda ingin menangkap output dari perintah dan waktu, Anda harus bekerja lebih keras. Menggunakan file sementara adalah salah satu solusinya. Sebenarnya, itu satu-satunya solusi yang andal jika Anda perlu menangkap kesalahan standar perintah dan output standarnya. Tetapi jika tidak, Anda dapat menangkap seluruh output dan memanfaatkan fakta bahwa time memiliki format yang dapat diprediksi (jika Anda menggunakan time -p untuk mendapatkan variabel format POSIX atau bash-specific TIMEFORMAT).

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*}

Jika Anda hanya peduli dengan waktu jam dinding, menjalankan date sebelum dan sesudah adalah solusi sederhana (jika sedikit lebih tidak tepat karena waktu ekstra yang dihabiskan untuk memuat perintah eksternal).

Jika Anda berada di bash (dan bukan sh) dan Anda TIDAK perlu akurasi sub-detik, Anda dapat melewatkan panggilan ke date seluruhnya dan melakukannya tanpa memunculkan tambahan proses, tanpa harus memisahkan output gabungan, dan tanpa harus menangkap dan mengurai output dari perintah apa pun:

# 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

Dengan waktu, output perintah keluar pada stdout dan waktu keluar pada stderr. Jadi, untuk memisahkannya, Anda dapat melakukan:

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

Tapi, sekarang waktunya ada dalam file. Saya tidak berpikir Bash dapat menempatkan stderr dalam suatu variabel secara langsung. Jika Anda tidak keberatan mengarahkan output perintah di suatu tempat, Anda dapat melakukan:

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

Ketika Anda melakukan ini, output perintah akan di outputfile dan waktu yang dibutuhkan untuk menjalankan akan di $FOO.

7
marinus

Dalam Menyatukan semua respons sebelumnya, saat dalam OSX

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

anda bisa suka

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

Untuk tujuan itu, mungkin lebih baik menggunakan times (daripada time) di bash yang mencetak akumulasi pengguna dan waktu sistem untuk Shell dan untuk proses yang dijalankan dari Shell, contoh menggunakan:

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

Lihat: help -m times untuk info lebih lanjut.

4
kenorb

Install /bin/time (mis. pacman -S time)

Jadi, bukannya kesalahan saat mencoba -f tanda:

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

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

Anda benar-benar dapat menggunakannya:

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

Dan dapatkan yang Anda inginkan - waktu dalam variabel (contoh menggunakan %e untuk waktu yang telah berlalu, untuk opsi lain periksa man time):

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

echo "$variable"
1

Sama seperti kepala ketika bekerja dengan pernyataan di atas dan terutama mengingat jawaban Grzegorz dalam pikiran. Saya terkejut melihat pada sistem Xenial Ubuntu 16.04 saya dua hasil ini:

$ 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

Saya tidak punya alias alias apa pun jadi saya tidak tahu mengapa ini terjadi.

1
Paul Pritchard

coba ini, ia menjalankan perintah sederhana dengan argumen dan menempatkan kali $ real $ user $ sys dan mempertahankan kode keluar.

Itu juga tidak bercabang subshell atau menginjak-injak variabel apa pun kecuali sys pengguna nyata, dan tidak mengganggu jalannya skrip

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
}

misalnya.

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

catatan: hanya kali perintah sederhana bukan pipa, yang komponennya dijalankan dalam subkulit

Versi ini memungkinkan Anda menentukan $ 1 nama variabel yang harus menerima 3 kali:

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
}

misalnya.

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

dan mungkin berguna jika akhirnya dipanggil secara rekursif, untuk menghindari terinjak-injak; tetapi kemudian rs dll harus dinyatakan lokal dalam penggunaannya.

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

0
Sam Liddicott