tags:

views:

273

answers:

7

Is there a way to embed the last command's elapsed wall time in a Bash prompt? I'm hoping for something that would look like this:

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

Background

I often run long data-crunching jobs and it's useful to know how long they've taken so I can estimate how long it will take for future jobs. For very regular tasks, I go ahead and record this information rigorously using appropriate logging techniques. For less-formal tasks, I'll just prepend the command with time.

It would be nice to automatically "time" every single interactive command and have the timing information printed in a few characters rather than 3 lines.

A: 

Will putting a \t in PS1 work for you?

It does not give the elapsed time but it should be easy enough to subtract the times when necessary.

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


Following the OP's comment that he is already using \t. If you can use tcsh instead of bash, you can set the time variable.

/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 >

You can change the format of the printing to be less ugly (se the tcsh man page).

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

I do not know of a similar facility in bash

Andrew Stein
At the moment, I am in fact using \t, and it's often sufficient. Unfortunately, when I'm running long commands non-interactively there are gaps of time from when a previous command completes and I interactively start a new one. If I press <ENTER> just before running a new command it works, but I don't always remember to do so, so I'd like to automatically capture elapsed time.
Mr Fooz
A: 

I don't think that this is possible. You can call an extra command when bash displays its prompt (PROMPT_COMMAND), but I wouldn't know of any way to call another program just before running a command. Of course, you could set your bash prompt to show the current time (which admittedly would be pretty useless if you had the prompt sitting there waiting for your command for half an hour).

innaM
As it turns out, I do show the current time in my prompt, but if I pause to analyze a command's output, then the timestamp difference includes my thinking time (which shouldn't count). Also, this doesn't work well when there's enough output that the previous timestamp is past the end of the scrollback buffer.
Mr Fooz
The DEBUG trap is executed before every command, including non-interactively run commands. It can be used to start a timer to measure execution time.
Ville Laurikari
+4  A: 

This is minimal stand-alone code to achieve what you want:

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]$ '
Ville Laurikari
+1 - have added a suggestion for the issue you identified.
martin clayton
Thanks Martin. I updated my answer to give a greatly improved solution taking advantage of the DEBUG trap.
Ville Laurikari
+5  A: 

You could utilize this zsh-borrowed hook for bash: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

Timing done with this hook (Mac OS X): Use Growl to monitor long-running shell commands

The MYYN
Here's another look at using Bash's debug hook in a similar manner: http://www.davidpashley.com/articles/xterm-titles-with-bash.html from fellow member David Pashley: http://serverfault.com/users/7342/david-pashley
Dennis Williamson
The hook was just what I was looking for (and the growl link is a nice quick usage example). Thanks!
Mr Fooz
A: 

Combine Ville's solution with this snippet that installs a DEBUG hook, as mentioned in other answers.

function reset_timer { 
   echo $SECONDS > $time_f;
}

trap 'reset_timer' DEBUG

That ensures that the time displayed is the command run time, rather than the time since the last prompt was displayed - gets around the issue Ville found.

martin clayton
A: 

Another very minimal approach is:

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

This shows the number of seconds since the last simple command was started. The counter is not reset if you simply hit Enter without entering a command -- which can be handy when you just want to see how long the terminal has been up since you last did anything in it. It works fine for me in Red Hat and Ubuntu. It did NOT work for me under Cygwin, but I'm not sure if that's a bug or just a limitation of trying to run Bash under Windows.

One possible drawback to this approach is that you keep resetting SECONDS, but if you truly need to preserve SECONDS as the number of seconds since initial shell invocation, you can create your own variable for the PS1 counter instead of using SECONDS directly. Another possible drawback is that a large seconds value such as "999999" might be be better displayed as days+hours+minutes+seconds, but it's easy to add a simple filter such as:

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)) # '

This translates "999999" into "11days,13:46:39". The sed at the end changes "1days" to "1day", and trims off empty leading values such as "0days,00:". Adjust to taste.

willdye
A: 

If you hadn't set up any of the other answers before you kicked off your long-running job and you just want to know how long the job took, you can do the simple

$ HISTTIMEFORMAT="%s " history 2

and it will reply with something like

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

and you can then just visually subtract the two timestamps (anybody know how to capture the output of the shell builtin history command?)

philsnow