Bash scripting - using dmenu to update note files

Published on
read
Bash scripting - using dmenu to update note files

Introduction

In this post, we are going to create a BASH script that utilises dmenu to easily sift through and select specific note files. This helps improve efficiency when one has several notes for various subjects, but not an easy way to search through and select them. So, let's get started. The tool, dmenu, for those unaware is a suckless tool, which searches for applications on a system, and lets the user search through using keyword searches. It also allows users to pipe data into dmenu and so is not limited to just applications. We shall be using this functionality within the script.

Requirements

For the script to function as required; we need a few things before we start writing the program:

  • dmenu
  • the directory containing our notes
  • the extension of the notes files
  • the application path to open the selected note

The script

For any BASH script, we first need to tell the file to use BASH as the interpreter, by placing the path to the BASH binary pre-fixed by a couple of important characters #! known as 'hashbang' or 'shebang'. Secondly,  we need to set the directory of the notes folder and store it within a variable:

#!/bin/bash

# Notes directory; alter to match your requirements.
notes_dir="$HOME/Documents/sync/md/"

Note: adding comments throughout the script, or any programming file is good practice. It allows others and yourself, down the line, to understand what is occurring within the script.


Moving on, we now need to store each note file within an array to pass this input into dmenu. There we will use the find command found on nearly every Linux system to accomplish this. This is also where the extension for the files comes into this. This will tell the command how exactly to find the file within the directory. The code will look as follows:

# Store all notes paths within an array.
notes_array="$(find ${notes_dir} -iname "*.md")"

Here, we are using find and providing it with a directory to look into, our saved variable, notes_dir . In addition, the -iname option is used which case-insensitively looks for all files with the .md extension. These files are then saved into the variable notes_array.  Alternatively, one can use the fd tool which is a find a replacement, written in rust. It is much faster, and will increase the speed of finding files. To use fd the code is not so dissimilar:

fd .md $HOME/Documents/sync/md/
# or
fd .md ${notes_dir}

# Stored within a variable
notes_array="$(fd .md ${notes_dir})"

Note: find as well as fd searches recursively within the specified folder for a specific pattern supplied.


Now we shall start work on the main function. The first thing to do is to define a function called main() within the script. Here, we shall set a variable named choice which will equal the selected path to the note directory. The files will first be sorted alphabetically. Dmenu provides the option of setting a prompt, which makes the interface slightly nicer to read. We shall store the contents of this within the variable prompt.

# Prompt for dmenu output
prompt="md notes"

function main()
{
    # Store selected note path within choice variable
    choice=$(printf '%s\n' "${notes_array}" | sort | dmenu -p "$prompt" ) || exit 1
}

Here, printf is used to print the contents (the file paths) of all the notes stored in notes_array. It uses C-style input parameters and is easy to use. Here, %s\n means that a string with a new line will be printed. It acts as the formatter/placeholder. The next parameter is the notes_array ; where each note path will be printed with a new line. The output is passed to sort and then dmenu . If the program fails, it shall exit with an exit code of 1 (unsuccessful operation). 


We shall now work on what happens when a certain note file is selected. To do this, an if statement will be used on the choice variable:

if [ "$choice" ]; then
     # Open note in desired application
     /usr/bin/marktext "${choice}"
else
     exit 0
fi

main

Here, if choice is selected, that is, a note file has been selected, then use marktext to open the file, else exit with an exit code of 0 (successful operation). Marktext, for those unaware, is an electron markdown editor used to open a markdown file. As all my notes are written in markdown, this is the application I will use for this script. However, one's notes may differ and so you may adjust the script to one's own requirements. The function main is then called at the end of the script.


Overall, the script should look something like this:

#!/bin/bash

# Notes directory; alter to match your requirements.
notes_dir="$HOME/Documents/sync/md/"

# Store all notes paths within an array.
notes_array="$(find ${notes_dir} -iname "*.md")"

# Prompt for dmenu output
prompt="md notes"

function main()
{
    # Store selected note path within choice variable
    choice=$(printf '%s\n' "${notes_array}" | sort | dmenu -p "$prompt" ) || exit 1

    if [ "$choice" ]; then
       # Open note in desired application
       /usr/bin/marktext "${choice}"
    else
       exit 0
  fi

}

main

Conclusion

At this point, the script is quite basic, but it is quite handy when there is a need to quickly open a notes file. Using simple BASH scripting techniques and neat, little tools like dmenu , one can make their workflow more efficient and productive. 

To see a further improved usage of this script, check out my GitLab repository, where I add the option of creating a new file from dmenu , and provide dmenu with some other options. 

Author

Discussion (0)

Subscribe