Bash(v4 +)スクリプト内の特定の操作にかかる時間を調べるために、time
コマンドからの出力を「個別に」解析し、(最終的に)Bash変数(let VARNAME=...
)。
今、私はtime -f '%e' ...
(またはBashビルトインのためにcommand time -f '%e' ...
)を使用していますが、実行したコマンドの出力をすでにリダイレクトしているので、どうすればいいのか迷っています。 time
コマンドの出力をキャプチャします。基本的にここでの問題は出力を分離するため of time
と実行されたコマンドの出力からです。
私が欲しいのは、コマンドの開始から完了までの時間を秒単位(整数)でカウントする機能です。 time
コマンドまたはそれぞれの組み込みコマンドである必要はありません。
編集:以下の2つの有用な回答を踏まえて、2つの説明を追加したいと思います。
これまでのところdate
を使用した解決策は、私が望むものに近づいています。
time
の出力をvarに取得するには、以下を使用します。
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
を使用することもできるため、実行の前後にそれを使用して、差分を計算します。
START=$(date +%s.%N)
command
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF
Bashでは、time
構成の出力は標準エラーに送られ、影響を受けるパイプラインの標準エラーをリダイレクトできます。それでは、出力とエラーストリームに書き込むコマンドから始めましょう:sh -c 'echo out; echo 1>&2 err'
。コマンドのエラーストリームをtime
からの出力と混同しないように、コマンドのエラーストリームを一時的に別のファイル記述子に転送できます。
{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; }
これは、out
をfd 1に、err
をfd 3に、時間を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:/')
Fd 2にerr
を、fd 3に時間を指定すると、2つのファイル記述子を直接交換する方法がないので面倒です。
{ { { 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:/')
これは、コマンドの出力を後処理する方法を示していますが、コマンドの出力とその時間の両方をキャプチャする場合は、さらに努力する必要があります。一時ファイルの使用は1つの解決策です。実際、コマンドの標準エラーとその標準出力の両方をキャプチャする必要がある場合は、これが唯一の信頼できるソリューションです。しかし、それ以外の場合は、出力全体をキャプチャして、time
が予測可能な形式であることを利用できます(time -p
POSIX形式 またはbash固有の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
を実行するのが簡単な解決策です(外部コマンドのロードに余分な時間がかかっているために少し不正確な場合)。
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.
時間とともに、コマンド出力は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
になります。
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"
}
そのためには、times
でtime
(bash
より)を使用する方がおそらく良いでしょう。これは、シェルおよびシェルから実行されたプロセスの累積ユーザーおよびシステム時間を出力します。例使用する:
$ (sleep 2; times) | (read tuser tsys; echo $tuser:$tsys)
0m0.001s:0m0.003s
見る: help -m times
詳細については。
/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"
上記のステートメントを処理するとき、特にグジェゴルツの答えを念頭に置いて頭を上げるのと同じように。私のUbuntu 16.04 Xenialシステムで、次の2つの結果に驚いた。
$ 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
エイリアスを設定していないので、なぜこれが起こっているのかわかりません。
これを試してください。引数を指定して単純なコマンドを実行し、時間$ real $ user $ sysを設定して、終了コードを保持します。
また、実際のユーザー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
そして、時間を踏みにじることを避けるために、それが再帰的に呼び出される場合に役立ちます。しかし、その場合、RUなどはローカルで宣言して使用する必要があります。
http://blog.sam.liddicott.com/2016/01/timeing-bash-commands.html