it-swarm-eu.dev

Jak změřit dobu provádění programu a uložit ji do proměnné

Aby bylo možné zjistit, jak dlouho určité operace v skriptu Bash (v4 +) zabírají, rád bych analyzoval výstup z příkazu time „samostatně“ a (nakonec) jej zachytil v proměnné Bash (let VARNAME=...).

Nyní používám time -f '%e' ... (nebo raději command time -f '%e' ... kvůli vestavěnému basu), ale protože už přesměrovávám výstup provedeného příkazu, jsem opravdu ztracen, jak bych se chystal zachytit výstup příkazu time. V zásadě je zde problém oddělit výstup z time od výstupu spuštěných příkazů.

Chci tedy funkčnost počítání času v sekundách (celá čísla) mezi spuštěním příkazu a jeho dokončením. Nemusí to být příkaz time nebo příslušný vestavěný.


pravit: vzhledem ke dvěma užitečným odpovědím níže jsem chtěl přidat dvě vysvětlení.

  1. Nechci zahodit výstup provedeného příkazu, ale nezáleží na tom, zda skončí na stdout nebo stderr.
  2. Upřednostňoval bych přímý přístup před nepřímým (tj. Přímý přístup k výstupu oproti jeho uložení do mezilehlých souborů).

Řešení pomocí date se zatím blíží tomu, co chci.

65
0xC0000022L

Chcete-li získat výstup time do var, použijte následující:

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

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

Můžete také jen požádat o jednorázový typ, např. utime:

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

Chcete-li získat čas, můžete také použít date +%s.%N, tak si to vezměte před a po provedení a vypočítejte rozdíl:

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

V bash, výstup konstrukce time jde k jeho standardní chybě, a můžete přesměrovat standardní chybu potrubí, které ovlivňuje. Začněme tedy příkazem, který zapisuje do výstupních a chybových streamů: sh -c 'echo out; echo 1>&2 err'. Abychom nespojili chybový proud příkazu s výstupem z time, můžeme dočasně převést chybový proud příkazu do jiného deskriptoru souboru:

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

Toto zapíše out do fd 1, err do fd 3 a časy do 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:/')

Bylo by příjemnější mít err na fd 2 a časy na fd 3, takže je vyměňujeme, což je těžkopádné, protože neexistuje žádný přímý způsob, jak zaměnit dva deskriptory souborů:

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

Ukazuje to, jak můžete zpracovat výstup příkazu, ale pokud chcete zachytit jak výstup příkazu, tak jeho časy, musíte pracovat tvrději. Jedním z řešení je použití dočasného souboru. Ve skutečnosti je to jediné spolehlivé řešení, pokud potřebujete zachytit standardní chybu příkazu i jeho standardní výstup. Ale jinak můžete zachytit celý výstup a využít skutečnosti, že time má předvídatelný formát (pokud používáte time -p pro získání proměnné formát POSIX nebo basové proměnné 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*}

Pokud vám záleží jen na době nástěnných hodin, spuštění date před a po je jednoduchým řešením (pokud je poněkud nepřesnější kvůli dodatečnému času strávenému načítáním externího příkazu).

Pokud jste v bash (a ne sh) a nepotřebujete přesnost sekundové přesnosti, můžete přeskočit hovor na date úplně a udělat to bez tření navíc procesy, aniž by bylo nutné oddělit kombinovaný výstup a aniž by bylo nutné zachytit a analyzovat výstup z příkazů:

# 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

Časem vychází příkazový výstup na stdout a čas na stderr. Chcete-li je oddělit, můžete:

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

Nyní je však čas v jednom souboru. Nemyslím si, že Bash je schopen vložit stderr přímo do proměnné. Pokud vám nevadí přesměrovat výstup příkazu někam, můžete:

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

Když to uděláte, výstup příkazu bude v outputfile a čas potřebný ke spuštění bude v $FOO.

7
marinus

V Dává dohromady všechny předchozí odpovědi, když v OSX

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

můžeš dělat rád

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

Za tímto účelem je pravděpodobně lepší použít times (než time) v bash, který vytiskne kumulované uživatelské a systémové časy pro Shell a pro procesy spuštěné z Shell, příklad použití:

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

Viz: help -m times pro více informací.

4
kenorb

Nainstalujte /bin/time (Např. pacman -S time))

Takže místo chyby při pokusu o označení -f:

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

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

Můžete ji skutečně použít:

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

A získejte to, co chcete - čas v proměnné (příklad používá pro skutečný uplynulý čas %e, U ostatních možností zkontrolujte man time):

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

echo "$variable"
1

Stejně jako heads up při práci s výše uvedenými výroky a zejména s ohledem na Grzegorzovu odpověď. Překvapilo mě, když jsem v mém systému Ubuntu 16.04 Xenial viděl tyto dva výsledky:

$ 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

Nemám nastaveny žádné aliasy, takže nevím, proč se to děje.

1
Paul Pritchard

zkuste to, spustí jednoduchý příkaz s argumenty a dá časy $ real $ user $ sys a zachová výstupní kód.

Rovněž nezkrácuje podskupiny ani neprošlapuje žádné proměnné kromě skutečných uživatelských sysů a jinak neinterferuje se spuštěním skriptu

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
}

např.

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

poznámka: jde pouze o jednoduchý příkaz, nikoli o potrubí, jehož komponenty jsou spuštěny v podsíti

Tato verze vám umožňuje zadat jako $ 1 název proměnných, které by měly obdržet 3krát:

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
}

např.

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

a může být užitečné, pokud bude nakonec nazýváno rekurzivně, aby nedocházelo k občasnému šlapání; ale pak by u jejich použití mělo být deklarováno místní.

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

0
Sam Liddicott