Use PS0 and PS1 to display the execution time of each bash command

It seems that by executing the code in the variables PS0 and PS1 (which are evaluated before and after running the prompt command, as I understand it), it should be possible to record the time of each command being executed and display it in a prompt.Something like this:

user@machine ~/tmp
$ sleep 1

user@machine ~/tmp 1.01s
$

      

However, I quickly got stuck with PS0 write times, as something like this doesn't work:

PS0='$(START=$(date +%s.%N))'

      

As I understand it, the assignment START

happens in the sub-shell, so it doesn't appear in the outer shell. How do you approach this?

+5


source to share


2 answers


I took it as a riddle and want to show the result of my puzzle:

At first I fiddled with timing. date +%s.%N

(which I didn't understand before) was where I started. Unfortunately, it seems that arithmetic evaluation bash

doesn't seem to support floating points. Thus, I chose something else:

$ START=$(date +%s.%N)

$ awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$START') }' /dev/null
8.059526s

$

      

This is enough to calculate the time difference.

Next, I confirmed what you already described: calling a sub-shell prevents the use of shell variables. So I thought about where else I could store a start time that is global to sub-shells, but local enough to be used across multiple interactive shells at the same time. My solution is pace. files (c /tmp

). To provide a unique name that I came up with this pattern: /tmp/$USER.START.$BASHPID

.

$ date +%s.%N >/tmp/$USER.START.$BASHPID ; \
> awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$(cat /tmp/$USER.START.$BASHPID)') }' /dev/null
cat: /tmp/ds32737.START.11756: No such file or directory
awk: cmd. line:1: BEGIN { printf("%fs", 1491297723.111219300 - ) }
awk: cmd. line:1:                                              ^ syntax error

$

      

Heck! Again I got into a shell problem. To get around this, I defined another variable:

$ INTERACTIVE_BASHPID=$BASHPID

$ date +%s.%N >/tmp/$USER.START.$INTERACTIVE_BASHPID ; \
> awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)') }' /dev/null
0.075319s

$

      

Next step: script this along with PS0

and PS1

. In a puzzle like this ( SO: How do I change the color of the bash prompt based on the exit code of the last command? ), I've already mastered the "quoting hell". Thus, I should be able to do it again:



$ PS0='$(date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}")'

$ PS1='$(awk "BEGIN { printf(\"%fs\", "$(date +%s.%N)" - "$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)") }" /dev/null)'"$PS1"
0.118550s
$

      

Ahh. It starts to work. So there is only one problem - finding the right script to run to initialize INTERACTIVE_BASHPID

. I found ~/.bashrc

which seems to be the correct one for this, and which I have already used in the past for some other personal settings.

So, putting it all together - these are the lines I added to mine ~/.bashrc

:

# command duration puzzle
INTERACTIVE_BASHPID=$BASHPID
date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}"
PS0='$(date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}")'
PS1='$(awk "BEGIN { printf(\"%fs\", "$(date +%s.%N)" - "$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)") }" /dev/null)'"$PS1"

      

3rd line (command date

) added to solve another problem. Comment this out and start a new interactive bash to see why.

A snapshot of my cygwin xterm with bash where I added the above lines in ./~bashrc

: Snapshot of my king xterm with bash prompt << / a

Notes:

  • I see this as a puzzle solution rather than a "serious productive" solution. I am sure that such a measurement of time takes a long time. The team time

    can provide a better solution: SE: How to execute script execution time efficiently? ... However, it was a good lecture for bash practice ...

  • Don't forget that this code pollutes your directory with a /tmp

    growing number of small files. Either clean up /tmp

    from time to time, or add appropriate commands to clean up (like before ~/.bash_logout

    ).

+2


source


I was looking for a solution to another problem and came across this question and figured it sounds like a cool feature. Using @Scheff's excellent answer as a basis, in addition to the solutions I developed for my other problem, I came up with a more elegant and fully featured solution.

I first created several functions that read / write time to / from memory. Writing to the shared memory folder prevents access to the disk and is not saved across reboots unless the files are cleaned up for some reason

function roundseconds (){
  # rounds a number to 3 decimal places
  echo m=$1";h=0.5;scale=4;t=1000;if(m<0) h=-0.5;a=m*t+h;scale=3;a/t;" | bc
}

function bash_getstarttime (){
  # places the epoch time in ns into shared memory
  date +%s.%N >"/dev/shm/${USER}.bashtime.${1}"
}

function bash_getstoptime (){
  # reads stored epoch time and subtracts from current
  local endtime=$(date +%s.%N)
  local starttime=$(cat /dev/shm/${USER}.bashtime.${1})
  roundseconds $(echo $(eval echo "$endtime - $starttime") | bc)
}

      

Input data for bash_ functions is the bash PID

These functions and the following are added to the ~ / .bashrc file

ROOTPID=$BASHPID
bash_getstarttime $ROOTPID

      

They create an initial time value and store the bash PID as another variable that can be passed to the function. Then you add functions to PS0 and PS1



PS0='$(bash_getstarttime $ROOTPID) etc..'
PS1='\[\033[36m\] Execution time $(bash_getstoptime $ROOTPID)s\n'
PS1="$PS1"'and your normal PS1 here'

      

Now it will generate time in PS0 before processing terminal input, and again generate time in PS1 after processing terminal input, then calculate the difference and add to PS1. Finally, this code clears the saved time when you exit the terminal:

function runonexit (){
  rm /dev/shm/${USER}.bashtime.${ROOTPID}
}

trap runonexit EXIT

      

Putting it all together and also testing additional code, it looks like this:

Terminal Output

The important parts are the runtime in ms and the user.bashtime files for all PIDs of active terminals stored in shared memory. The PID is also displayed immediately after the input from the terminal, since I added its display to PS0 and you can see the bashtime files being added and removed.

PS0='$(bash_getstarttime $ROOTPID) $ROOTPID experiments \[\033[00m\]\n'

      

0


source







All Articles