Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can the last command's wall time be put in the Bash prompt?

Tags:

bash

time

prompt

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.

like image 877
Mr Fooz Avatar asked Dec 07 '09 20:12

Mr Fooz


People also ask

How do I change the $PS1 prompt in Linux bash?

You can change the BASH prompt temporarily by using the export command. This command changes the prompt until the user logs out. You can reset the prompt by logging out, then logging back in.

How do you time a bash script?

Using Bash Shell's TIMEFORMAT The TIMEFORMAT is a string format that will be printed after the execution of the block code inside the time{} wrapper finishes. The %R specifies to print the elapsed time in seconds with milliseconds precision. Let's test our script: $ ./elapsed_time.sh It took 12.008 seconds.

Which command in bash executes the last?

1) Ctrl + P Just press the Ctrl and P keys together to fill the prompt with the last executed command and you are ready to go. This method works in bash perfectly even after closing the terminal, but it might not work in zsh after closing the session.

How do I set the bash prompt?

To change your Bash prompt, you just have to add, remove, or rearrange the special characters in the PS1 variable. But there are many more variables you can use than the default ones. Leave the text editor for now—in nano, press Ctrl+X to exit.


3 Answers

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]$ '
like image 52
Ville Laurikari Avatar answered Oct 19 '22 21:10

Ville Laurikari


Using your replies and some other threads, I wrote this prompt which I want to share with you. I took a screenshot in wich you can see :

  • White : Last return code
  • Green and tick mark means success (return code was 0)
  • Red and cross mark means error (return code was >0)
  • (Green or Red) : Last command execution time in parenthesis
  • (Green or Red) : Current date time (\t)
  • (Green if not root, Red if root) : the logged username
  • (Green) : the server name
  • (Blue) : the pwd directory and the usual $

Custom prompt

Here is the code to put in your ~/.bashrc file :

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'
like image 44
Nicolas Thery Avatar answered Oct 19 '22 22:10

Nicolas Thery


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.

like image 37
willdye Avatar answered Oct 19 '22 22:10

willdye