Quickly go to a directory

In this article I show you a set of scripts I use daily to quickly go to any directory inside my home directory ($HOME ou ~). For example to go to the directory ~/Quand/Te/Reverrai/Je/Pays/Merveilleux/Où/Ceux/Qui/S/Aiment/Vivent/A/Deux, I just have to activate a shortcut and, using dmenu, type qua deux.

Video illustrating these scripts use
Example of these scripts use

These scripts work on Linux and can easily be translated into BSD or MacOS systems if needed.

These scripts are not in my scripts repository because there are not independent enough. Each script is not useful by itself and you need to adapt how they link yourself.

Why not use locate?

By studying my solution you may think that the command locate is a better alternative.

The command locate is used to find system directories and not directories which are inside the home directory. Without spending a lot of time of configuration, using locate cannot achieve what we are trying to do here.

Displaying the directories

My script display-home-directories displays all the visible directories and subdirectories present in the home directory. I also want to exclude some directories, for that I put in a configuration file regular expressions corresponding to the paths to exclude.

display-home-directories
#!/bin/sh

# Displays all the visible directories in the home directory that should be
# listed. Remove the $HOME prefix in the paths.

# The configuration file contains regular expressions for the paths that should
# not be listed, one by line. Lines starting with a # are ignored.

# Configuration example:
#
# # Exclude "obj", "bin" and "node_modules" directories and their
# # sub-directories.
# /(obj|bin|node_modules)$
# /(obj|bin|node_modules)/

CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/display-home-directories"

paths=$(find $HOME -type d -not -path '*/\.*' -print |
    sed "1d" |  # Remove the home directory
    sed "s+^$HOME/++" |  # Remove the $HOME prefix in each path
    sort)

if [ -f "$CONFIG_FILE" ]; then
    while IFS="" read -r regex; do
        # Skip empty lines and comments (lines starting by #)
        if [ -z "$regex" ] || [ -z "${regex%%#*}" ]; then
            continue
        fi
        paths=$(echo "$paths" | grep --invert-match --extended-regexp "$regex")
    done < "$CONFIG_FILE"
fi

echo "$paths"

In my configuration file, at ~/.config/display-home-directories, I exclude automatically generated directories like node_modules, bin and obj directories. The regular expressions are in the example in the script comments. I start the regular expressions by a / so directories ending, for example, by a bin (Images/Rabbin or Images/Ebin) are not excluded.

For some directories, I like to list the first level subdirectories but not their subdirectories. For example for the ~/Foo directory, I put in my configuration file this regular expression.

^Foo/[^/]+/

The ~/Foo/Bar directory is displayed by not the ~/Foo/Bar/Quux directory.

Caching

The script execution time increase with the number of directories present. The best thing to do is to create a cron task to cache the results. To do that, do crontab -e in a shell environment to edit the cron tasks, do not forget to put the full path of the script in order to avoid problems if it is not in the path listed in the PATH variable used by cron.

*/10 * * * * $HOME/bin/display-home-directories > $HOME/.cache/display-home-directories

Do not forget to create the cache file before using it as we will do right now!

Going to a directory

I use dmenu to select the directory I want to go to. dmenu is a small software displaying a given menu with a search bar used to filter the results. My script go-to-directory manages the directory selection and goes to this directory.

go-to-directory
#!/bin/sh

# cd into the chosen directory.
# If a command is given as an argument, use this command.

CACHE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/display-home-directories"
choice=$(cat $CACHE_FILE | dmenu -l 20 -i)

if [ -n "$choice" ]; then
    if [ "$#" -eq 0 ]; then
        cd "$HOME/$choice"
    else
        "$1" "$HOME/$choice"
    fi
fi

In this script, I activate the -i option of dmenu so searches are case-insensitive.

I use sxhkd as keyboard shortcuts manager and caja as file manager. In my sxhkdrc I have these line to open caja in a chosen directory:

sxhkdrc
super + j
    $HOME/bin/go-to-directory caja

To cd when I use a shell session, I have this alias:

alias c='source go-to-directory'

Drawbacks

Using dmenu has two big drawbacks.

To remove the need of having X, I thought about using choice but there are too much drawbacks (from the short period of time I tested it).

Even if there are some minor listed drawbacks, this is nothing compared to the convenience given by the solution described in this article. Now that I am used of using my scripts, I wonder why there are no alternatives to my scripts set by default in other systems!