Tracking your time spent in the terminal
Published: 2022-01-23 | Last Updated: 2022-01-25 | ~19 Minute Read
Table of Contents
Motivation
At the end of 2021 I noticed many people posting some information from their social media accounts, like Spotify and others, about the details of their usage on those applications. I don’t use some of these services because I prefer open source alternatives, however the idea to look back and see your behavior on a given platform/application over time is really interesting to me.
Most of the applications I use don’t have that type of feature and I don’t mind, but for some others I’d like to know some stats of my usage to drive decisions about habits or general behavior tweaks. Tracking yourself can prove to be useful to better yourself or simply to know more about how you operate in general.
An example of the power of tracking yourself is Stephen Wolfram’s personal analytics which showcase many different data points from his life which he then analyzes. Like in the previous example this practice can allow us to use data in a way that could potentially bring meta-information to light that would otherwise be very difficult to obtain.
Inspired by this, I thought it’d be nice to track my activities on the terminal over time. I decided to create a tool for this since I was not able to find anything that did this from the terminal in a way that worked for me. A complex system like Wolfram Alpha is definitely not the goal, but I’ll try to make a tool that I can easily use and that provides enough insight for me to consider useful.
Requirements
I expect this tool to fulfill the following list of requirements:
- Terminal based
- Bash scripted
- Simple to use
I think the first two are pretty self explanatory but I’d like to elaborate a bit on the third requirement. I’ve seen other tools for this task, mainly TimeWarrior, Pomogoro and Timetrace, however they seem to focus on to-do or freelancing style functionality. I have a much simpler workflow in mind for my activity tracking so I will focus on that.
I would mainly want to track things I do via the terminal, i.e. vim
, ssh
. These two tools are the main focus for me right now since I use the Vim editor to write blog posts, like this one, and software development.
I expect to be able to see how many hours I spend working on programming tasks, regardless of what they’re related to, during the year as well as writing/researching blog posts. Similarly I want to track how long I spend on SSH sessions since I’m starting a new project that will require a bit of time over SSH.
Past experience
I have tried several mobile time tracking applications in the past where you can to track pretty much anything and see the data nicely organized. One I used for a while and really liked was ATimeTracker, and another one that I used briefly, and look forward to using again once this feature is implemented, is Track-and-graph.
These apps are great for general habit tracking, however for tracking activities when I’m on my desktop what happens is I often forget to start/stop the timers and data becomes inconsistent.
There are plenty of time trackers for the desktop, however I feel like they add overhead to my workflow and slow me down with the additional logging in, clicking around and such.
Expected workflow
I would like my workflow to be simple, there should not be any overhead to whatever I’m doing. I would like to avoid things like maintenance and preparation/setup to begin tracking a given task.
One thing I have seen from other tools is that there is a lot of setup and configuration prior to being able to track your activities. I understand that this is necessary in some, probably most, situations but my use case is a bit different.
I would like to simply begin a coding session for example and have the tracker stop automatically when I close the application, that way I avoid forgetting to stop it after a long session of programming. The same applies for SSH, I expect to connect via SSH and work on things and when I close the session the tracker will stop tracking that activity.
After tracking my usage over time for a given task I want the ability to check statistics of my activity when I choose to.
Plan
So far I have the following:
- Use shell and command line Linux utilities
- Start the tracking when the terminal program starts
- Stop the tracker when the terminal program stops
- Write results to a text file
- Parse the data in the created file to show statistics
Solution
What I have come up with so far is a shell function that uses the time
utility to track the process. This approach has the benefit of being able to stop tracking when the process exits, which is handy.
This is the simple .bashrc
function I have so far:
# Takes two parameters, 1.command to track 2.tag associated with the task
function track {
# Pass the first cli argument to the time program
# Ouput the time results to the time-tracking.txt file
{ time $1 ; } 2>> ~/time-tracking.txt
# Append the date to the time output
date >> ~/time-tracking.txt
# Append the last cli argument as the task name
echo "task: ${@: -1}" >> ~/time-tracking.txt
}
The user can pass two parameters to the function:
- Command to execute/track
- Task category aka
tag
I plan to group things into categories such as programming
or blog-post
so that I can easily view the statistics for these groups. This can be easily customized on the fly by using specific tag
s as the last argument.
As an example one could further specify tag
names based on their own naming conventions or more established ones. The BEM methodology comes to mind as one that could be adapted. As a practical example of this blog-post-tracker
, programming-golangapp
would be tag
s that provide further details.
Here is a sample execution:
bash-5.1$ track vim blog-post
This needs absolutely no setup, I simply run the command as I usually would with the added flags and the tracking is started, and once I exit the vim
program tracking ends.
This is the resulting entry in the time-tracking.txt
file:
bash-5.1$ cat time-tracking.txt
real 6m2.391s
user 0m0.035s
sys 0m0.003s
Sat Jan 22 22:08:15 CST 2022
task: blog-post
I would like to keep only the real
time from the time
command output only so I created a second function to remove the additional entries that looks like the following:
# Clean up the user and sys entries from the time command
function cleanup() {
sed -i "/^user/d" ~/time-tracking.txt
sed -i "/^sys/d" ~/time-tracking.txt
}
I then call the cleanup
function from within the track
function and the time-tracking.txt
file output now looks like so:
bash-5.1$ cat time-tracking.txt
real 6m2.391s
Sat Jan 22 22:08:15 CST 2022
task: blog-post
This is the updated track
function:
# Takes two parameters, 1.command to track 2.tag associated with the task
function track {
# Clean up the user and sys entries from the time command
function cleanup() {
sed -i "/^user/d" ~/time-tracking.txt
sed -i "/^sys/d" ~/time-tracking.txt
}
# Pass the first cli argument to the time program
# Ouput the time results to the time-tracking.txt file
{ time $1 ; } 2>> ~/time-tracking.txt
# Append the date to the time output
date >> ~/time-tracking.txt
# Append the last cli argument as the task name
echo "task: ${@: -1}" >> ~/time-tracking.txt
# Run the cleanup function
cleanup
}
So with that and after calling it a few times for different tasks, we have a file with entries like the following:
bash-5.1$ cat time-tracking.txt
real 6m2.391s
Sat Jan 22 22:08:15 CST 2022
task: blog-post
real 0m10.709s
Sat Jan 22 22:13:24 CST 2022
task: blog-post
real 1m24.624s
Sat Jan 22 22:18:08 CST 2022
task: programming
Additionally if we wanted to track something like firefox
with additional flags instead of vim
which I use as a simple one word command we would track it like so:
bash-5.1$ track firefox https://nixing.mx browsing
This yields the expected output in the time-tracking.txt
file:
real 0m0.023s
Sat Jan 22 23:53:49 CST 2022
task: browsing
The last space separated parameter will be taken as the tag
to be added to the time-tracking.txt
file, again this makes it simple in terms of maintenance and setup overhead.
Statistics
Now that we have a way to track our activities on the terminal we can parse the file to gather some data. I have not had much time to work on this yet but I created a, partially working, new function stats
where we can parse the file however we want.
The stats
function looks like the following:
# Takes one parameter, the tag the user wants to get stats for
function stats {
# Process the time-tracking.txt file, remove the date line and add the minutes for the specified task
cat time-tracking.txt | grep -v Sat | sed -n "/^task: $1/{x;p;d;}; x" | grep real | sed "s/real//g" | \
awk -F '[ms]' '{print $1 " minutes " $2 " seconds"}' | \
awk '{sum+=$3 ; print $0} END{print "sum:", sum, "secs ", " avg:", sum / NR, "secs"}'
}
When we call this function for the blog-post
tag we get the following:
bash-5.1$ stats blog-post
6 minutes 2.391 seconds
0 minutes 10.709 seconds
sum: 13.1 secs avg: 6.55 secs
For a different tag:
bash-5.1$ stats programming
1 minutes 24.624 seconds
sum: 24.624 secs avg: 24.624 secs
Finally if we run the stats function without any parameters we get the time sum in minutes of all tasks regardless of tag:
bash-5.1$ stats
6 minutes 2.391 seconds
0 minutes 10.709 seconds
1 minutes 24.624 seconds
sum: 37.724 secs avg: 12.5747 secs
Next steps
I still need to work on the stats
parsing and perhaps on adding more details to the track
entries. I will continue to update here as I make progress but I think most of the core functionality I expected was achieved by these simple functions.
I would like to add statistics for month and day of the week next in order to see which tasks I worked on the most during a given period etc.
Update Jan 24th
I have worked a bit more on this and fixed a few bugs and added another function. I noticed that my previous version of the stats
function was not counting the minutes in the result, only the seconds.
I also made some changes to the track
function to better format the data that’s written to time-tracking.txt
, below are the updates for these two functions:
Small updates were done to the track
function:
# Takes two parameters, 1.command to track 2.tag associated with the task
function track {
# Clean up the user and sys entries from the time command
function format() {
# Delete the user and sys lines
sed -i "/^user/d ; /^sys/d" ~/time-tracking.txt
# Prepend date: to the date line
sed -i "s/^\([A-Z][a-z][a-z]\)/date: \1/g" ~/time-tracking.txt
# Prepend time: to the time line
sed -i 's/^real[[:blank:]]/time: /g' time-tracking.txt
}
# Pass the first cli argument to the time program
# Ouput the time results to the time-tracking.txt file
{ time $1 ; } 2>> ~/time-tracking.txt
# Append the date to the time output
date >> ~/time-tracking.txt
# Append the last cli argument as the task name
echo "task: ${@: -1}" >> ~/time-tracking.txt
# Run the cleanup function
format
}
The contents of the time-tracking.txt
file after the updates:
bash-5.1$ head time-tracking.txt
time: 6m2.391s
date: Sat Jan 22 22:08:15 CST 2022
task: blog-post
time: 0m10.709s
date: Sat Jan 22 22:13:24 CST 2022
task: blog-post
This is the updated stats
function:
# Takes one parameter, the tag the user wants to get stats for
function stats {
# Process the time-tracking.txt file
# Remove the date line
grep -v "^date:" ~/time-tracking.txt | \
# Remove the task line and print line above it
# This will get the time for the specified tag
sed -n "/^task: $1/{x;p;d;}; x" | \
# Remove the time: prepended text
sed -n "s/^time: //p" | \
# Print the time entries for minutes and seconds
awk -F '[ms]' '{print $1 " minutes " $2 " seconds"}' | \
# Add the minutes/seconds columns for the specified task
awk '{sum1 += $3 ; print $0 }
{sum2 += $1 }
END {print "sum:", sum2, "mins", sum1, "secs ", " avg:", sum2 / NR, "mins", sum1 / NR, "secs"}'
}
When running the stats
function now:
bash-5.1$ stats blog-post
6 minutes 2.391 seconds
0 minutes 10.709 seconds
sum: 6 mins 13.1 secs avg: 3 mins 6.55 secs
I have also added another function graph
. This function graphs the number of tracked activities per week day. The idea behind this is to see which days of the week I’m most active.
I shamelessly grabbed the code shared on this stack overflow question as a baseline and tweaked it a bit.
This what the new graph
function looks like:
# Graphs the tracked activities per day of the week
function graph {
# Generates a tmp file for the graph function to use
function graphdata {
# Create an array with weekdays
WEEKDAYS=(Mon Tue Wed Thu Fri Sat Sun)
# Loop over the weekdays
for DAY in "${WEEKDAYS[@]}"; do
# Grep each weekday from the list
grep $DAY time-tracking.txt | \
# Count the results and write to graphtmpfile.txt
wc -l >> ~/graphtmpfile.txt
done
}
# Call the graphdata function
graphdata
# Set variables for bold text messages
NORMAL=$(tput sgr0)
BOLD=$(tput bold)
# The data in ~/graphtmpfile.txt should be one value per line.
# Get highest value from the file
MAXVALUE=$( sort -nr ~/graphtmpfile.txt | head -n1 )
# Get single digit numbers from ~/graphtmpfile.txt
SINGLEDIGIT=$(sed -n "/^[0-9]$/p" ~/graphtmpfile.txt)
# Get double digit numbers from ~/graphtmpfile.txt
DOUBLEDIGIT=$(sed -n "/^[0-9][0-9]$/p" ~/graphtmpfile.txt)
# Get triple digit numbers from ~/graphtmpfile.txt
TRIPLEDIGIT=$(sed -n "/^[0-9][0-9][0-9].*/p" ~/graphtmpfile.txt)
# Check if the value is over 100
if [ $MAXVALUE -ge 100 ]
then
# Set the highest value for chart height
# divide by 10 to make each line represent 10
CHARTHEIGHT=$(( $MAXVALUE / 10 ))
else
# Set the highest value for chart height
# leave value as is
CHARTHEIGHT=$MAXVALUE
fi
# Line parsing variable
CHARTLINE=$CHARTHEIGHT
# Empty line for readability
echo
# Loop through all the lines in the ~/graphtmpfile.txt file
while [ $CHARTLINE -gt 0 ]; do
# Subtract one from the CHARTLINE variable
(( CHARTLINE-- ))
# Subtract REDUCTION from the VALUE variable
REDUCTION=$(( $MAXVALUE*$CHARTLINE/$CHARTHEIGHT ))
# Add line height to the graph
echo | awk -v num=$CHARTLINE 'BEGIN{ line=num+1} { printf "%-4s", line}'
# Loop through all lines of the file
while read VALUE; do
# Set the VALUE variable value
VALUE=$(( $VALUE-$REDUCTION ))
# Determine VALUE is less or equal to zero
if [ $VALUE -le 0 ]; then
# Print spaces
echo -n " "
# If value is greater than zero
else
# Print graph characters
echo -n "||| "
fi
done < ~/graphtmpfile.txt
echo
done
# Print the days of the week under results
echo " Mon Tue Wed Thu Fri Sat Sun"
# Print a warning message when there is a big disparity
# in the number of tasks between days
if [ $MAXVALUE -ge 100 ] && [ $SINGLEDIGIT -ge 1 ]
then
echo
echo "${BOLD}Note:${NORMAL} Source data contains mixed triple and single digit data "
echo " this graph is most likely inaccurate"
echo
fi
# Delete the ~/graphtmpfile.txt file
rm ~/graphtmpfile.txt
}
When running the graph function this is the output:
bash-5.1$ graph
13 |||
12 |||
11 |||
10 ||| |||
9 ||| |||
8 ||| |||
7 ||| |||
6 ||| ||| |||
5 ||| ||| |||
4 ||| ||| |||
3 ||| ||| |||
2 ||| ||| ||| ||| |||
1 ||| ||| ||| ||| ||| ||| |||
Mon Tue Wed Thu Fri Sat Sun
Each set of |||
characters represent one tracked activity for the day. From the graph above, on Tuesday for example there was only one activity tracked, while on Sunday there were 13.
When the numbers grow into triple digits the graph
function will divide them by 10 so that each bar represents 10 tracked items.
I plan to expand the graph
function to be able to show months and maybe weeks so you can easily glance at when you were most active during the year.
These few functions have become large enough to warrant their own script which I will be working on next, so keep an eye out for that in my git repositories if you’re interested in tracking yourself from the terminal.
Conclusion
It’s a good thing that I had this idea near the beginning of the year so I can start tracking early on. Hopefully I’ll be able to showcase how long I have spent on activities like programming and blog writing by the end of the year.
Once there is more functionality implemented I will most likely make it a standalone script and publish it on github like I do with Bourne to Blog.