Creating a smart alternative to 'cd' command

📅️ Published: August 3, 2020  • 🕣 4 min read

Creating a smart alternative to 'cd' command

Do you sometimes forget the actual location of directories & have to juggle through cd & ls to know the right path? In this short post, we discuss how to make a “smart” alternative to cd command on Linux (& probably MacOS 🙄).

The problem is simple we just need to automate this task of finding the actual location of directories and the 2 most popular commands to do that are find & locate:

1. locate

  • Used to search for files, much faster than find.
  • Limited functionality.
  • Depends on external database for fast finding (/var/lib/mlocate/mlocate.db).
    # search file paths which exactly match the pattern, "memes"
    locate -r '/memes$' | grep $HOME
    

2. find

  • Extensive options.
  • Can search for both files & directories.
  • Slower than locate.
    # search for directory "memes" inside your home dir.
    find -O2 $HOME -name "memes" -type d
    

Ok then basics clear, now we write a small bash function to create scd (smart cd).


scd() {
    if [[ $1 != "" ]]; then
        while read -r value; do
            if [[ -d $value ]]; then
                printf "%s\n" "Hit 🎯: $value"
                cd "$value"
            fi
        done < <( locate -e -r "/$1$" | grep "$HOME" )
    else
        cd "$HOME" || exit
    fi
}

That’s it, 12 liner (a little) better alternative to cd. Now you can add it to your .bashrc or better create a .bash_functions file and source it in your default shell config.

if [ -f ~/.bash_functions ]; then
    . ~/.bash_functions
fi

Note: If you are on Mac, locate would probably be not available, use find instead.

Not yet convinced? Ok, a more advance version with options like .., -

scd() {
    if [[ $1 != "" ]]; then
        case $1 in
            [".."]* ) cd .. ;;
            ["-"]* ) cd - ;;
            ["/"]* ) cd / ;;
            * ) while read -r value; do
                    if [[ -d $value ]]; then
                        printf "%s\n" "Hit 🎯: $value"
                        cd "$value"
                    fi
                done < <( locate -e -r "/$1$" | grep "$HOME" ) ;;
        esac
    else
        cd "$HOME" || exit
    fi
}

Cons 😤

  • Well, one of the problems with our bash function is that we wouldn’t be able to leverage “tab” auto-suggestions.. Read More
  • Newly created directories won’t be readily available with the locate command as it depends on its own database mlocate.db. The database is updated automatically by our system through a cron job. Although you can still do it manually.
    sudo updatedb
    

Pros 📈

  • Don’t have to search for directory paths & cd into it.
  • Even if scd switches to the wrong directory (in case of multiple matches), you will still be able to see what’s the actual path in output & then switch to it manually.

what do you think?


UPDATE: 9 Aug 2020

The CDPATH

Thanks to Santosh’s comment on my LinkedIn post there is another way of switching directories “smartly”.

CDPATH can be used as a search path for the cd command. A colon-separated list of directories in which the shell looks for destination directories specified by the cd command.

It gives you a limited functionality to cd into sub-directories of a specific parent directory.

For e.g in your .bashrc or .zshrc add this line.

export CDPATH=".:/home/bhupesh/Desktop"

And now you can freely switch directories inside Desktop & would not have to use cd ~/Desktop/dirB or cd /home/username/Desktop/dirB. Well of-course now you need to do this manually & find out which directories you usually spent more time in.


UPDATE: 17 Aug 2020

Tab Auto-suggestions

Thanks to Thamara’s comment, I recently added “tab” suggestions powered by bash_completions.

scd demo gif

How to use ?

  1. Start a new bash session.
  2. Source the completions script source scd-completions.bash. Get it from here.
  3. Create a new version of scd function :
     scd() {
         if [ -z "$1" ]; then
             echo "No directory path provided"
             exit 2
         else
             echo "$1"
             cd "$1" || exit
         fi
     }
    
  4. Create a .inputrc file in your HOME directory with following options enabled. Read More about these options.

     set show-all-if-ambiguous on
     set completion-ignore-case on
     set completion-map-case on
     set show-all-if-unmodified on
     set menu-complete-display-prefix on
     "\t": menu-complete% 
    
  5. scd <search-string>[TAB][TAB]

Pressing [TAB] one time triggers the completion. Pressing it twice will automatically complete the command.

Tired of fucking up with git everyday?

ugit helps you undo git commands with ease. Undo from 15+ git scenarios

ugit logo