A Developer's Diary: Building A Notes Taking App in Shell

Publié le 2018-11-08, à l'origine sur marmelab.com

Something I really like about being a developer is that you learn all the time: a pattern, a lib, an obscure configuration trick... In the heat of the action, you're glad, but a few days later, you often forget. It is at these time that you think it would have been a good idea to take notes.

I've already tried some notepads: jrnl, but I've never been able to remember the commands, boostnote that I don't use when I code because it's an extra window, or gist but I can't keep it organized...

And this summer, I received this link "did.txt file" file in my changelog newsletter.


Here is how Patrick introduces his blog post:

Goal: create an insanely simple “did” file accessible by terminal

And that's right, it's very simple (it's just adding an alias in your .bash_profile or .zshrc) and bloody effective :

alias did="vim +'normal Go' +'r!date' ~/did.txt"

A did command opens a file into the terminal - so you don't leave your working environment - with the current date. All you have to do is write down this little thing you've just learned.

did : the original

And I really liked the idea of having a new tool built only with what is already present on the system. But in fact, it's a little too simple. For example, here is what happens if you use did twice on the same day:

Maybe too simple

Two problems made me think that I would not integrate this command as it stands in my daily routine:

This post documents how I customized this good idea to my needs. I tried to keep the same simplicity as the original did and continuing to use only what was already available in the terminal.

One logbook per week

I work in a two-week time box (sprint), so cutting the single file into several weekly logbooks was obvious.

I'll not go into the implementation's details, but show you the (almost) final result. The --help option, man and Google were my friends to get this result.

export DID_PATH=~/.did

function did(){
    export LC_ALL=C
    if [ ! -f ${DID_PATH}/$(date +%Y-%V).txt ]; then
        echo "Week $(date +"%V (%B %Y)") \n\n$(date +"%A %Y-%m-%d")" > ${DID_PATH}/$(date +%Y-%V).txt
    FILE_EDITION_DATE="$(stat -c "%y" ${DID_PATH}/$(date +%Y-%V).txt)"
    NOW="$(date +"%Y-%m-%d")"
    if [ ${FILE_EDITION_DATE:0:10} != ${NOW} ]; then
        echo "\n$(date +"%A %Y-%m-%d")\n" >> ${DID_PATH}/$(date +%Y-%V).txt
    unset LC_ALL
    vim +'normal Go' ${DID_PATH}/$(date +%Y-%V).txt

Here are the points that seem important to me.

the new did

This new command gets the job done since now it creates one file per week instead of a single file. But this improvement would also be a good example for David Kadavy's article "Complexity is creepy: It’s never just one more thing”.

Indeed, my one more thing brings its share of questions:

View a specific logbook : didv (view)

function didv(){
    if [ $1 ]
         cat ${DID_PATH}/${1}.txt
        if [ ! -f ${DID_PATH}/$(date +%Y-%V).txt ]; then
            LC_ALL=C echo "# Week $(date +"%V (%B %Y)") \n\n## $(date +"%A %Y-%m-%d")" > ${DID_PATH}/$(date +%Y-%V).txt
        cat ${DID_PATH}/$(date +%Y-%V).txt

This function is simpler than did's, but it introduces the use of command arguments: if [ $1 ]. didv opens the current log and didv 2018-32 the log for week 32.
cat is in charge of displaying the file.

Display logbooks with didv

List weekly logbooks: didl (list)

I thought that setting up the list of logs would be the fastest feature to set up. I pragmatically tested the ls and tree commands :

list logs with ls and tree

But two things bothered me:

Display the month as from the week number with date has been the most complicated part of that did improvement day!

function week2Month(){
    export LC_ALL=C
    year=$(echo $1 | cut -f1 -d-)
    week=$(echo $1 | cut -f2 -d-)
    local dayofweek=1 # 1 for monday
    date -d "$year-01-01 +$(( $week * 7 + 1 - $(date -d "$year-01-04" +%w ) - 3 )) days -2 days + $dayofweek days" +"%B %Y"
    unset LC_ALL

function didl(){
    for file in `ls ${DID_PATH}/*.txt | sort -Vr`; do
        filenameRaw="$(basename ${file})"
        echo "${filename} ($(week2Month ${filename}))"


And here we are at the last feature to implement: search the logs. It's grep that is involved.

function dids(){
    export LC_ALL=C
    if [ $1 ]
        for file in `ls ${DID_PATH}/*.txt | sort -Vr`; do
            NB_OCCURENCE="$(grep -c @${1} ${file})"
            if [ ${NB_OCCURENCE} != "0" ]
                filenameRaw="$(basename ${file})"
                echo -e "\n\e[32m=> ${filename} ($(week2Month ${filename}), ${NB_OCCURENCE} results) \e[0m" && grep -n -B 1 ${1} ${file}
         echo "You must add a something to search..."
    export LC_ALL=C

To be able to tag notes and limit the search to these tags, I decided to use a tag's prefix @, allowing to do NB_OCCURENCE="$(grep -c @${1} ${file})". The second use of grep no longer uses this prefix, allowing to display all the lines corresponding to the searched word.


Formatting notes

I was close to the goal! I no longer had one, but 4 commands:

Only one point was still pending:

The file is in.txt format, which severely limits the possibilities of formatting, such as code extracts.

A markup language is perfectly adapted for that: markdown.

Markdown everywhere

No luck, there's no basic tool in the terminal to process and display a .md file. However, I had set myself a rule:

"..., and continuing to use only what was already available in the terminal."

It doesn't matter, I'm a punk.

So I found some projects that met the need :

I preferred the vmd rendering. All that remained was to modify all the .txt to .md, add some # and replace cat by vmd in the didv function.

didv in markdown

The final scripts



I don't know if my scripts can be useful to you. If so, I would be happy to. Otherwise, I would also be happy anyway.

Because it's not the script that's important here. What I would like to have shared in this post is the pleasure of building your own little tool from what is available on your system. It's really very fun! During that day spent modifying the original did.txt, I learned a lot, tested a lot and came up with a result that was exactly what I needed. No more, no less.
It was a bit of a low-dev. I'm very sensitive to low-tech these days.

So I hope this reading has given you some ideas. As far as I'm concerned, I think I'm going to quickly add a didp command.

Did you guess it? p for publishing! Now that I have log books in markdown, it shouldn't be very complicated to publish them on a server, and then add a search engine like Algolia to index them.