프로그램 실행 시간을 측정하고 변수 내에 저장하는 방법


61

Bash (v4 +) 스크립트 내에서 특정 작업이 얼마나 오래 걸리는지 알아보기 위해 time"별도의"명령 의 출력을 구문 분석하고 Bult 변수 ( let VARNAME=...) 내에서 (궁극적으로) 캡처하고 싶습니다 .

이제는 time -f '%e' ...(또는 command time -f '%e' ...Bash 내장으로 인해) 사용하고 있지만 이미 실행 된 명령의 출력을 리디렉션하기 때문에 명령의 출력을 캡처하는 방법에 대해 정말로 잃어 버렸습니다 time. 기본적 여기서 문제는 출력을 분리하기 의가 time실행 된 명령어의 출력에서.

내가 원하는 것은 명령 시작과 완료 사이의 시간 (초)을 계산 하는 기능 입니다. time명령 또는 해당 내장 장치 일 필요는 없습니다 .


편집 : 아래 두 가지 유용한 답변이 주어지면 두 가지 설명을 추가하고 싶었습니다.

  1. 실행 된 명령의 출력을 버리고 싶지 않지만 stdout 또는 stderr로 끝나는지는 실제로 중요하지 않습니다.
  2. 간접 접근 방식에 대한 직접적인 접근 방식을 선호합니다 (즉, 중간 파일에 저장하는 대신 출력을 직접 잡기).

date지금까지 사용한 솔루션 은 내가 원하는 것에 가깝습니다.


데이터를 얻을 여전히 사용하여 C 프로그램에서 작업을 수행하는 것입니다 정상적으로 실행시키는 동안 그것을 처리하는 가장 직접적인 방법 fork(), execvp()wait3()/wait4(). 이것은 궁극적으로 시간과 친구가하는 일입니다. 파일이나 비슷한 접근 방식으로 리디렉션하지 않고 bash / perl에서 간단한 방법을 알지 못합니다.
penguin359

흥미로운 질문이 여기에 있습니다 .
Caleb

@Caleb : 감사합니다. 실제로 흥미 롭습니다. 그러나 내 목적을 위해서는 스크립트로 충분합니다.
0xC0000022L

답변:


85

timevar로 출력을 얻으려면 다음을 사용하십시오.

usr@srv $ mytime="$(time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$mytime"

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

utime과 같은 단일 시간 유형을 요청할 수도 있습니다.

usr@srv $ utime="$( TIMEFORMAT='%lU';time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$utime"
0m0.000s

를 사용할 수있는 시간을 얻으려면 date +%s.%N실행 전후에 시간을 계산하고 diff를 계산하십시오.

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

4
그래도 명령에서 출력을 버리고 싶지 않았습니다. 그래서 세 번째 코드 블록은 내가 생각한 것과 가장 가깝습니다. 마지막 DIFF=$((END-START))으로을 쓰지만 산술 표현식을 사용합니다. :) ... 답변 해주셔서 감사합니다. +1
0xC0000022L

1
@STATUS_ACCESS_DENIED : Bash는 부동 소수점 산술을 수행하지 않으므로 ( .%Nbinfalse의 코드에서) 두 번째 해상도보다 낫기를 원한다면 더 bc계산하거나 더 계산 해야합니다 .
Gilles

@Gilles : 알고 있습니다. 내 질문에 쓴 것처럼 정수는 좋습니다. 초보다 더 높은 해상도를 가질 필요가 없습니다. 그러나 감사합니다. 날짜 호출을 변경해야합니다. 그러나 나는 그것을 깨달았다.
0xC0000022L

6
참고로 % N 날짜 형식은 Mac OS X에서 작동하지 않는 것 같습니다. 단지 "N"만 반환합니다. 우분투에서 좋아.
David

time (cmd) 2> something타이밍 출력을로 리디렉션 하는 작업은 file(설명서에 따라) 의미가 없으며 time키워드가있는 다른 쉘에는 없으며 버그로 간주 될 수 있습니다 . 향후 버전에서는 작동하지 않을 수 있으므로 의존하지 않습니다 bash.
Stéphane Chazelas

17

bash에서 time구문 의 출력은 표준 오류로 이동하고 영향을받는 파이프 라인의 표준 오류를 리디렉션 할 수 있습니다. 출력 및 오류 스트림에 쓰는 명령부터 시작하겠습니다 sh -c 'echo out; echo 1>&2 err'. 명령의 오류 스트림을의 출력과 섞지 않기 위해 time명령의 오류 스트림을 다른 파일 설명 자로 일시적으로 전환 할 수 있습니다.

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

이 기록 out, 1 전략 중을 err3 전략 중하고, 시간은 2 전략 중하기 :

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

이 더 적절한 것 errFD 2 및 FD 3 시간에, 그래서 우리는 두 개의 파일 디스크립터를 교환하는 직접적인 방법이 없기 때문에 복잡하다, 그들을 교환 :

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

이것은 명령의 출력을 후 처리하는 방법을 보여 주지만 명령의 출력과 시간을 모두 캡처하려면 더 열심히 노력해야합니다. 임시 파일을 사용하는 것이 하나의 솔루션입니다. 실제로 명령의 표준 오류와 표준 출력을 모두 캡처해야하는 경우이 솔루션이 유일하게 신뢰할 수있는 솔루션입니다. 그러나 그렇지 않으면 전체 출력을 캡처 하고 POSIX 형식 또는 bash 특정 변수 를 얻는 데 time사용 time -p하는 경우 예측 가능한 형식 의 사실을 활용할 수 있습니다 .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*}

벽시계 시간 만 신경 쓰는 경우 date전후 명령을 실행 하는 것이 간단한 해결책입니다 (외부 명령을로드하는 데 추가 시간이 소요되어 약간 더 부정확 한 경우).


와우, 테스트 할 것이 많습니다. 광범위한 답변에 감사드립니다. +1.
0xC0000022L

7

시간이 지남에 따라 명령 출력이 stdout에서 나오고 시간이 stderr에서 나옵니다. 따라서 분리하려면 다음을 수행하십시오.

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

그러나 지금 시간은 파일입니다. Bash가 stderr을 변수에 직접 넣을 수 있다고 생각하지 않습니다. 명령 출력을 어딘가에 리디렉션하지 않으려면 다음을 수행하십시오.

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

이렇게하면 명령의 출력이 outputfile시작되고 실행하는 데 걸리는 시간이입니다 $FOO.


1
오, 나는 몰랐다. 나는 timestderr에 쓰는 것을 알고 있었지만 stdout과 실행 된 명령의 stderr을 stdout에 병합한다는 것을 알지 못했습니다. 그러나 일반적으로 직접적인 방법이 없습니다 (즉, 중간 파일이 없음)? +1.
0xC0000022L

3
: @STATUS_ACCESS_DENIED time의 명령의 병합하지 않습니다 stdoutstderr. 내가 보여준 방법은 명령의 표준 출력 만 필요하다고 가정했습니다. bash에서 나오는 내용 만 저장 하므로 stdout리디렉션해야합니다. 명령의 stderr를 안전하게 삭제할 수있는 경우 시간은 항상 stderr의 마지막 줄에 있습니다. 명령의 출력 스트림이 모두 필요한 경우 다른 스크립트로 래핑하는 것이 좋습니다.
marinus

6

에 있고 bash( sh초도 아니고 ) 1 초 미만의 정확도가 필요 date하지 않은 경우 추가 된 프로세스를 생성하지 않고 결합 된 출력을 분리하지 않고 출력을 캡처 및 구문 분석하지 않고도 호출을 완전히 건너 뛸 수 있습니다. 모든 명령에서 :

# 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.

좋은. 나는 전에 SECONDS에 대해 들어 본 적이 없다
Bruno9779

이것이 어떤 Bash 버전으로 시작되었는지 알고 있습니까?
0xC0000022L

4

이를 위해 그것을 사용하는 것이 아마도 더 나은 times(이상 time에) bash있는 쉘과 쉘, 예를 들어, 사용에서 실행되는 프로세스에 대한 누적 된 사용자 및 시스템 시간을 출력한다 :

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

자세한 내용은 참조하십시오 help -m times.


4

OSX에있을 때 이전 응답을 모두 모으기

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

당신은 좋아할 수 있습니다

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

1

설치 /bin/time(예 pacman -S time)

따라서 -f플래그를 시도 할 때 오류 대신 :

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

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

실제로 사용할 수 있습니다.

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

그리고 원하는 시간을 변수로 가져옵니다 (예 : %e다른 옵션 확인을 위해 실제 경과 시간에 사용 man time).

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

echo "$variable"

/usr/bin/time많은 시스템에서
Basile Starynkevitch

자세히 보면 내 질문에서 지적했습니다. timeBash에 내장 된 셸 (및 아마도 다른 셸)이지만 time실행 파일 경로 가에 있는 한 내장 명령과 달리 외부 명령을 실행하는 데 PATH사용할 수 있습니다 command time.
0xC0000022L

1

위의 진술을 다루고 특히 Grzegorz의 대답을 염두에두면 머리가 위로 향합니다. 우분투 16.04 Xenial 시스템 에서이 두 가지 결과를 보게되어 놀랐습니다.

$ 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

별칭을 설정하지 않았으므로 왜 이런 일이 발생하는지 알 수 없습니다.


1
time내장 쉘이기도합니다.
Basile Starynkevitch

명령을 실행하려면을 사용 command하고 내장 기능을 실행하려면을 사용하십시오 builtin. 여기 마법이 없습니다. 사용 help가능한 명령 type <command>을 파악 하거나 유형, 유형 <command>(예 : 내장, 외부 명령, 기능 또는 별명) 을 파악하는 데 사용하십시오 .
0xC0000022L

바 실러, 당신 말이 맞아요 나는 command명령 의 중요성을 발견하지 못했습니다 . 실제로 / usr / bin / time이 호출되도록 보장합니다. 이것은 다음 두 문장은 호환되는 것을 의미합니다 : $ command time -f %e sleep 4$ /usr/bin/time -f %e sleep
폴 프 리처드에게

0

이것을 시도하면 인수와 함께 간단한 명령을 실행하고 $ real $ user $ sys 시간을두고 종료 코드를 유지합니다.

또한 실제 사용자 시스템을 제외한 모든 변수에서 서브 쉘을 포크하거나 짓밟 지 않으며, 스크립트 실행을 방해하지 않습니다.

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

예 :

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

참고 : 파이프 라인이 아닌 간단한 명령 만 수행합니다.

이 버전을 사용하면 3 번받는 변수의 이름을 $ 1로 지정할 수 있습니다.

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

예 :

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

시간이 걸리는 것을 피하기 위해 재귀 적으로 호출되는 경우 유용 할 수 있습니다. 그러나 rus 등은 사용시 로컬로 선언해야합니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.