web-dev-qa-db-ja.com

最後のコマンドの実時間はどのようにしてBashプロンプトに入れることができますか?

最後のコマンドの経過壁時間を Bash プロンプトに埋め込む方法はありますか?私はこのように見える何かを望んでいます:

[last: 0s][/my/dir]$ sleep 10
[last: 10s][/my/dir]$

背景

私は頻繁に長いデータ処理ジョブを実行しますが、それらがどれくらいの時間を要したかを知ることは、将来のジョブにかかる時間を見積もることができるので便利です。非常に定期的なタスクの場合、私は先に進み、適切なロギング手法を使用してこの情報を厳密に記録します。あまり形式的でないタスクの場合は、コマンドの前にtimeを付けます。

すべてのインタラクティブコマンドを自動的にtimeし、タイミング情報を3行ではなく数文字で印刷すると便利です。

59
Mr Fooz

このzshで借用したフックをbashに利用できます: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

このフックで行われるタイミング(Mac OS X): Growlを使用して長時間実行されるシェルコマンドを監視する

10
miku

これは、必要なことを実現するための最小限のスタンドアロンコードです。

function timer_start {
  timer=${timer:-$SECONDS}
}

function timer_stop {
  timer_show=$(($SECONDS - $timer))
  unset timer
}

trap 'timer_start' DEBUG
Prompt_COMMAND=timer_stop

PS1='[last: ${timer_show}s][\w]$ '
94
Ville Laurikari

あなたの返信と他のいくつかのスレッドを使用して、私はあなたと共有したいこのプロンプトを書きました。私はあなたが見ることができるスクリーンショットを撮りました:

  • 白:最後の戻りコード
  • 緑とチェックマークは成功を意味します(戻りコードは0でした)
  • 赤と十字マークはエラーを意味します(戻りコードは> 0でした)
  • (緑または赤):括弧内の最後のコマンド実行時間
  • (緑または赤):現在の日時(\ t)
  • (ルートでない場合は緑、ルートの場合は赤):ログに記録されたユーザー名
  • (緑):サーバー名
  • (青):pwdディレクトリと通常の$

Custom Prompt

〜/ .bashrcファイルに入れるコードは次のとおりです。

function timer_now {
    date +%s%N
}

function timer_start {
    timer_start=${timer_start:-$(timer_now)}
}

function timer_stop {
    local delta_us=$((($(timer_now) - $timer_start) / 1000))
    local us=$((delta_us % 1000))
    local ms=$(((delta_us / 1000) % 1000))
    local s=$(((delta_us / 1000000) % 60))
    local m=$(((delta_us / 60000000) % 60))
    local h=$((delta_us / 3600000000))
    # Goal: always show around 3 digits of accuracy
    if ((h > 0)); then timer_show=${h}h${m}m
    Elif ((m > 0)); then timer_show=${m}m${s}s
    Elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s
    Elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s
    Elif ((ms >= 100)); then timer_show=${ms}ms
    Elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms
    else timer_show=${us}us
    fi
    unset timer_start
}


set_Prompt () {
    Last_Command=$? # Must come first!
    Blue='\[\e[01;34m\]'
    White='\[\e[01;37m\]'
    Red='\[\e[01;31m\]'
    Green='\[\e[01;32m\]'
    Reset='\[\e[00m\]'
    FancyX='\342\234\227'
    Checkmark='\342\234\223'


    # Add a bright white exit status for the last command
    PS1="$White\$? "
    # If it was successful, print a green check mark. Otherwise, print
    # a red X.
    if [[ $Last_Command == 0 ]]; then
        PS1+="$Green$Checkmark "
    else
        PS1+="$Red$FancyX "
    fi

    # Add the ellapsed time and current date
    timer_stop
    PS1+="($timer_show) \t "

    # If root, just print the Host in red. Otherwise, print the current user
    # and Host in green.
    if [[ $EUID == 0 ]]; then
        PS1+="$Red\\u$Green@\\h "
    else
        PS1+="$Green\\u@\\h "
    fi
    # Print the working directory and Prompt marker in blue, and reset
    # the text color to the default.
    PS1+="$Blue\\w \\\$$Reset "
}

trap 'timer_start' DEBUG
Prompt_COMMAND='set_Prompt'
19
Nicolas Thery

もう1つの非常に最小限のアプローチは次のとおりです。

trap 'SECONDS=0' DEBUG
export PS1='your_normal_Prompt_here ($SECONDS) # '

これは、最後の単純なコマンドが開始されてからの秒数を示しています。コマンドを入力せずにEnterキーを押すだけでは、カウンターはリセットされません。これは、最後に何かを実行してから端末が稼働していた時間を確認したい場合に便利です。 RedHatとUbuntuでは問題なく動作します。 Cygwinでは機能しませんでしたが、それがバグなのか、WindowsでBashを実行しようとする際の制限なのかはわかりません。

このアプローチの考えられる欠点の1つは、SECONDSをリセットし続けることですが、最初のシェル呼び出しからの秒数としてSECONDSを保持する必要がある場合は、SECONDSを直接使用する代わりに、PS1カウンター用に独自の変数を作成できます。もう1つの考えられる欠点は、「999999」などの大きな秒の値が日+時間+分+秒として表示されやすいことですが、次のような単純なフィルターを簡単に追加できます。

seconds2days() { # convert integer seconds to Ddays,HH:MM:SS
  printf "%ddays,%02d:%02d:%02d" $(((($1/60)/60)/24)) \
  $(((($1/60)/60)%24)) $((($1/60)%60)) $(($1%60)) |
  sed 's/^1days/1day/;s/^0days,\(00:\)*//;s/^0//' ; }
trap 'SECONDS=0' DEBUG
PS1='other_Prompt_stuff_here ($(seconds2days $SECONDS)) # '

これは、「999999」を「11days、13:46:39」に変換します。最後のsedは、「1days」を「1day」に変更し、「0days、00:」などの空の先行値を削除します。好みに合わせて調整します。

11
willdye

長期にわたるジョブを開始する前に他の回答を設定しておらず、ジョブにかかった時間を知りたい場合は、簡単な操作を行うことができます。

$ HISTTIMEFORMAT="%s " history 2

そしてそれは次のようなもので返信します

  654  1278611022 gvn up
  655  1278611714 HISTTIMEFORMAT="%s " history 2

次に、2つのタイムスタンプを視覚的に差し引くことができます(シェルの組み込み履歴コマンドの出力をキャプチャする方法を知っている人はいますか?)

6
philsnow

Ville Laurikariから回答を受け取り、timeコマンドを使用してそれを改善し、1秒未満の精度を示しました。

function timer_now {
  date +%s%N
}

function timer_start {
  timer_start=${timer_start:-$(timer_now)}
}

function timer_stop {
  local delta_us=$((($(timer_now) - $timer_start) / 1000))
  local us=$((delta_us % 1000))
  local ms=$(((delta_us / 1000) % 1000))
  local s=$(((delta_us / 1000000) % 60))
  local m=$(((delta_us / 60000000) % 60))
  local h=$((delta_us / 3600000000))
  # Goal: always show around 3 digits of accuracy
  if ((h > 0)); then timer_show=${h}h${m}m
  Elif ((m > 0)); then timer_show=${m}m${s}s
  Elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s
  Elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s
  Elif ((ms >= 100)); then timer_show=${ms}ms
  Elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms
  else timer_show=${us}us
  fi
  unset timer_start
}

trap 'timer_start' DEBUG
Prompt_COMMAND=timer_stop

PS1='[last: ${timer_show}][\w]$ '

もちろん、これにはプロセスを開始する必要があるため、効率は低下しますが、気付かないほど高速です。

4
Thomas

見つけた trap ... DEBUGは毎回実行されていました$Prompt_COMMANDが呼び出され、タイマーがリセットされたため、常に0が返されました。

しかし、historyが時間を記録していることがわかり、これらを利用して答えを得ました。

HISTTIMEFORMAT='%s '
Prompt_COMMAND="
  START=\$(history 1 | cut -f5 -d' ');
  NOW=\$(date +%s);
  ELAPSED=\$[NOW-START];
  $Prompt_COMMAND"
PS1="\$ELAPSED $PS1"

しかし、それは完璧ではありません。

  • historyがコマンドを登録しない場合(たとえば、繰り返されるコマンドや無視されるコマンド)、開始時刻は間違っています。
  • 複数行のコマンドは、historyから適切に抽出された日付を取得しません。
3
PJSCopeland

Bash 4.x以降の別のアプローチは、以下のようにcoprocPS0およびPS1とともに使用することです。

cmd_timer()
{
    echo $(( SECONDS - $(head -n1 <&"${CMD_TIMER[0]}") ))
}

coproc CMD_TIMER ( while read; do echo $SECONDS; done )
echo '' >&"${CMD_TIMER[1]}" # For value to be ready on first PS1 expansion
export PS0="\$(echo '' >&${CMD_TIMER[1]})"
export PS1="[ \$(cmd_timer) ] \$"

これは.bashrc対応のスニペットです。 ndistract-me を使用するすべての人にとって特に便利です。これは独自の目的でtrap DEBUGを上書きします。

1

これは私のバージョンです

  • 日付を使用して時刻をフォーマットします。計算日のみ
  • 端末タイトルを設定する
  • pS1でユーザー$ + root#に\ $を使用します
  • 戻りコード/終了コードを表示する
  • dSTを無効にするには、date-uを使用します
  • _fooのような隠し名前を使用する
_x_dt_min=1 # minimum running time to show delta T
function _x_before {
    _x_t1=${_x_t1:-$(date -u '+%s.%N')} # float seconds
}
function _x_after {
    _x_rc=$? # return code
    _x_dt=$(echo $(date -u '+%s.%N') $_x_t1 | awk '{printf "%f", $1 - $2}')
    unset _x_t1
    #_x_dt=$(echo $_x_dt | awk '{printf "%f", $1 + 86400 * 1001}') # test
    # only show dT for long-running commands
    # ${f%.*} = int(floor(f))
    (( ${_x_dt%.*} >= $_x_dt_min )) && {
        _x_dt_d=$((${_x_dt%.*} / 86400))
        _x_dt_s='' # init delta T string
        (( $_x_dt_d > 0 )) && \
            _x_dt_s="${_x_dt_s}${_x_dt_d} days + "
        # format time
        # %4N = four digits of nsec
        _x_dt_s="${_x_dt_s}$(date -u -d0+${_x_dt}sec '+%T.%4N')"
        PS1='rc = ${_x_rc}\ndT = ${_x_dt_s}\n\$ '
    } || {
        PS1='rc = ${_x_rc}\n\$ '
    }
    # set terminal title to terminal number
    printf "\033]0;%s\007" $(tty | sed 's|^/dev/\(pts/\)\?||')
}
trap '_x_before' DEBUG
Prompt_COMMAND='_x_after'
PS1='\$ '

サンプル出力:

$ sleep 0.5
rc = 0
$ sleep 1
rc = 0
dT = 00:00:01.0040
$ sleep 1001d
rc = 0
dT = 1001 days + 00:00:00.0713
$ false
rc = 1
$ 
0
Mila Nautikus

これがトーマスに対する私の見解です」

date +%s%3Nを使用してミリ秒を基本単位として取得し、次のコードを簡略化します(ゼロを除く)

function t_now {
    date +%s%3N
}

function t_start {
    t_start=${t_start:-$(t_now)}
}

function t_stop {
    local d_ms=$(($(t_now) - $t_start))
    local d_s=$((d_ms / 1000))
    local ms=$((d_ms % 1000))
    local s=$((d_s % 60))
    local m=$(((d_s / 60) % 60))
    local h=$((d_s / 3600))
    if ((h > 0)); then t_show=${h}h${m}m
    Elif ((m > 0)); then t_show=${m}m${s}s
    Elif ((s >= 10)); then t_show=${s}.$((ms / 100))s
    Elif ((s > 0)); then t_show=${s}.$((ms / 10))s
    else t_show=${ms}ms
    fi
    unset t_start
}
set_Prompt () {
t_stop
}

trap 't_start' DEBUG
Prompt_COMMAND='set_Prompt' 

次に、PS1に$t_showを追加します

0
Ju Tutt

PS1に\ tを入れることはあなたのために働きますか?

経過時間はわかりませんが、必要に応じて時間を引くのは簡単です。

$ export PS1='[\t] [\w]\$ '
[14:22:30] [/bin]$ sleep 10
[14:22:42] [/bin]$

彼はすでに\ tを使用しているというOPのコメントに続いて。 bashの代わりにtcshを使用できる場合は、時間変数を設定できます。

/bin 1 > set time = 0
/bin 2 > sleep 10
0.015u 0.046s 0:10.09 0.4%      0+0k 0+0io 2570pf+0w
/bin 3 >

印刷のフォーマットを変更して、見苦しさを軽減することができます(tcshのmanページを参照)。

/bin 4 > set time = ( 0 "last: %E" )
/bin 5 > sleep 10
last: 0:10.09
/bin 6 >

私はbashで同様の施設を知りません

0
Andrew Stein