Skip to main content

Artisanal Scripting: Todo Lists and Reminders

Davidheadshot David Alberto

Header (17)

Photo by Glenn Carstens-Peters on Unsplash

There are many tools to keep todo lists and calendar reminders, so this post isn’t necessarily going to be about a groundbreaking way of doing such things. Rather, I’ll illustrate how easy it can be to build your own artisanal alternatives and have them within your terminal’s reach.

Before we continue, a little bit about my setup on macOS:

  • I use screen and tmux on a daily basis.
  • My preferred editor is vim, and I already mentioned my heavy use of git in previous blogs.
  • In tmux, I keep several sessions, each with a few windows, and I’m constantly splitting panes, breaking panes, joining panes, etc. I mainly use screen to run tmux inside one screen and to keep an alternate screen that runs sleep timers and other infrequent tasks.

The main point here is that I open multiple bash processes. This is relevant because printing the todo list every time I open a new shell would be the best place to have it visible.

My todo script is pretty simple. If called with no parameters, it appends the current date to a file that opens in vim at the last line. If called with parameters, they are appended to that file with the current timestamp. Either option ends with committing the changes to a git repository, of course.

todo list printed when a new bash process is started

In my .bashrc file, I print the contents of the file. Before printing, I pipe it through sed to remove blank lines, lines with comments, and lines that start with a space to make it easy to mark items as “done” and have them no longer print every time I open bash. Once I’m ready to forget about an item, I can safely remove it from the file knowing that git will keep it in the history.

Contents of calendar, with Indie Time/Learning Time every other Monday, and Infrastructure and Product Demos every other Friday

For reminders, I use the calendar command. This works by adding events to the ~/.calendar/calendar file. Running the calendar command prints events occurring soon. These reminders are all-day events, but that’s still pretty useful. For example, Appian encourages us to use 10% of our time to work on side projects (Indie Time) and 5% of our time to learn something new (Learning Time). The way I have these set up is that every other Monday I work on Indie Time and on other Mondays I work on Learning Time half of the day. Additionally, every other Friday we have product demos, which means we have a Happy Hour / Engineering Social. On alternate Fridays, we have infrastructure demos.

To tie this up with the mechanism that shows my todo list, every time I open a new tmux pane, I added a call to the calendar command in my .bash_profile script.

Over time, I’ve learned that while screen invokes .bashrc every time a new screen is opened, tmux actually runs .bash_profile. This is because tmux by default runs a login shell and screen doesn’t. To make them behave the same, I configured tmux to not start a login shell every time. This way, I can put the time-consuming initialization commands on .bash_profile and only keep the fast initialization portion in .bashrc. If starting a new tmux pane is painfully slow, that might be something to consider.

That’s pretty much it! This is how I get reminders on the terminal. Here’s what the scripts look like:


if ! pgrep -q leave; then
  grep -v -F -f <(sed -E 's/^#//;s/^[[:blank:]]+//;/^$/d' ~/Desktop/todo.txt) <(calendar) >> ~/Desktop/todo.txt
  leave +0800

[ -e ~/Desktop/todo.txt ] && sed '/^#/d;/^$/d;/^ /d' ~/Desktop/todo.txt

# When opening a new pane we want it to be an interactive, non-login shell so 
# .bashrc is run instead of .bash_profile, upon exit this won't run .bash_logout
set-option -g default-command "bash -i"

Monday+1	Indie Time
Monday+2	Learning Time
Monday+3	Indie Time
Monday+4	Learning Time
Friday+1	Infrastructure Demos
Friday+2	Product Demos
Friday+3	Infrastructure Demos
Friday+4	Product Demos

#!/usr/bin/env bash

[ ~/Desktop/todo.txt -ef ~/Dropbox/home/todo/todo.txt ] || ln -f ~/Dropbox/home/todo/todo.txt ~/Desktop/todo.txt

if [ $# -gt 0 ]; then
  echo $(date) $* >> ~/Desktop/todo.txt
  echo $(date) >> ~/Desktop/todo.txt
  vim $(wc -l ~/Desktop/todo.txt | awk '{print $2 " +" $1}')
touch ~/Dropbox/home/todo/todo.txt
batch <<'EOF'
git -C ~/Dropbox/home/todo commit --quiet -am "$(date)" > $tmp 2>&1 || cat $tmp
git -C ~/Dropbox/home/todo push --quiet origin HEAD > $tmp 2>&1 || cat $tmp
rm $tmp


Written by

David Alberto

David is a Lead Software Engineer at Appian.