Bash:経過時間を測定するためにコマンドをラップする方法は?

Bash:経過時間を測定するためにコマンドをラップする方法は?

経過時間を測定するためにコマンドをどのようにラップできますか?

現在私は以下を使用していますeval

do_cmd_named()
{
  local name=$1
  local cmd=$2

  echo "$name"
  local start_time=$(date +%s)
  eval "$cmd 2>&1"
  local exit_status=$?
  local end_time=$(date +%s)
  local elapsed_time_sec=$((end_time-start_time))
  local elapsed_time_min_sec=$(date -ud "@$elapsed_time_sec" +'%M:%S')
  if [[ $exit_status -ne 0 ]]
  then
    echo "$name failed with exit status $exit_status (elapsed time $elapsed_time_min_sec)"
    return $exit_status
  else
    echo "$name done (elapsed time $elapsed_time_min_sec)"
  fi
}

job()
{
    sleep 1
}

do_cmd_named "do job" "job"

その結果は次のとおりです。

do job
do job done (elapsed time 00:01)

私の場合、このアプローチほぼ働くただし、この方法はいくつかの規則に違反しているため、悪いと見なされます。バッシュFAQ。たとえば、「変数にコードを入れないでください」は、以下に由来します。バッシュFAQ #50(あなたも見ることができますバッシュFAQ #48)。

だから質問は:これを正しく行う方法は?

答え1

  1. コマンド(および引数)をキャプチャして大量にそれから避けることができますeval
  2. Bash 組み込み変数の使用$SECONDS
do_cmd_named() {
    local name=$1
    shift
    local -a cmd=("$@")

    echo "$name"
    SECONDS=0

    "${cmd[@]}"

    local status=$?
    local elapsed_time_sec=$SECONDS

    # print messages ...
}

do_cmd_named "job name" job arg1 arg2

$ SECONDSの問題は、ネストされた呼び出しの場合は$ SECONDSが0にリセットされることです。

ええ、これは本当です。

別のアプローチは、関数の先頭でSECONDSの現在の値を取得し、最後でいくつかの算術を実行することです。

一例:

rectest() {
    local n=$1 delay=$2 start=$SECONDS
    ((n == 3)) && return
    sleep $delay
    rectest $((n + 1)) $((delay + 3))
    echo "at level $n, delay is $delay and duration is $((SECONDS - start))"
}

実行すると、15秒後にすべての出力が表示されます。

$ rectest 0 2
at level 2, delay is 8 and duration is 8
at level 1, delay is 5 and duration is 13
at level 0, delay is 2 and duration is 15

答え2

zshに切り替えるオプションがある場合:

$ TIMEFMT='%J done (elapsed time: %E)'
$ time sleep 1
sleep 1 done (elapsed time: 1.00s)

関連情報