I do a lot of things in shell, and I'd like to have an easy and convenient way to keep a list of shortcuts/bookmarks. I wasn't satisfied by all of the existing solutions I found, but eventually I've come across a great, universal tool: command-line fuzzy finder by Junegunn Choi. Great thanks to Junegunn for that shell-life-changing tool.
It primarily allows you to “fuzzy-find” files (check the rich gif animation by the link above), but it also allows to feed arbitrary text data to it and filter this data. So, the shortcuts idea is simple: all we need is to maintain a file with paths (which are shortcuts), and fuzzy-filter this file. Here's how it looks: we type cdg
command (from “cd global”, if you like), get a list of our bookmarks, pick the needed one in just a few keystrokes, and press Enter. Working directory is changed to the picked item:
So, the first step is to install the aforementioned fzf: go to its page and follow its installation procedure.
After that, we need to have a simple mechanism for feeding our bookmarks to fzf. There are definitely much more than one way to do that, so you can come up with your better ideas, but I've done it in this rather simple way:
It's probably convenient to have two files with bookmarks: the system-wide one, and the second one for each user. Format of these files is like this:
/path/to/first_bookmark # probably some comment /path/to/second_bookmark /path/to/third_bookmark
First of all, we create helper script that echoes contents of given file with comments and empty lines stripped. Let's name it cdscuts_list_echo
and put to /usr/bin
:
#!/bin/bash cat $1 | sed 's/#.*//g' | sed '/^\s*$/d'
And then make it executable:
$ sudo chmod a+x /usr/bin/cdscuts_list_echo
Now, let's write script /usr/bin/cdscuts_glob_echo
that reads predefined files with bookmarks and echoes their contents together:
#!/bin/bash system_wide_filelist='' user_filelist='' if [ -r /etc/cdg_paths ]; then system_wide_filelist=$(cdscuts_list_echo /etc/cdg_paths) fi if [ -r ~/.cdg_paths ]; then user_filelist=$(cdscuts_list_echo ~/.cdg_paths) fi echo -e "$system_wide_filelist\n$user_filelist" | sed '/^\s*$/d'
As you see, the system-wide bookmarks are stored in /etc/cdg_paths
, and user's bookmarks are in ~/.cdg_paths
. Again, mark this script as executable:
$ sudo chmod a+x /usr/bin/cdscuts_glob_echo
Now, we can actually create our bookmark file ~/.cdg_paths
with the following test contents:
/path/to/first_bookmark # probably some comment /path/to/second_bookmark /path/to/third_bookmark
And check that cdscuts_glob_echo
successfully echoes it without comments and empty lines:
$ cdscuts_glob_echo ~/.cdg_paths /path/to/first_bookmark /path/to/second_bookmark /path/to/third_bookmark
And if we feed that to fzf
, we can already fuzzy-find in that list! Try it:
$ cdscuts_glob_echo ~/.cdg_paths | fzf
But, well, when we actually pick an item from the list, it is just printed on the console. It's surely very nice, but we would like to cd
to it instead.
The cd
operation cannot be implemented in standalone script (because each process has its own working directory, and therefore cd
is a built-in shell command). So, what we need is to write a shell function. Let's name it cdg
. For bash, open your ~/.bashrc
(and for zsh, open your ~/.zshrc
), and add the following:
# Setup cdg function # ------------------ unalias cdg 2> /dev/null cdg() { local dest_dir=$(cdscuts_glob_echo | fzf ) if [[ $dest_dir != '' ]]; then cd "$dest_dir" fi } export -f cdg > /dev/null
Then, open new shell session.
And that's it! Now, you can type:
$ cdg
Then pick an item from your list, and when you select it (by pressing Enter), your shell will try to change directory there. You can now fill your ~/.cdg_paths
with real paths and enjoy it.
You can find a lot of interesting stuff you can do with fzf
, check its page for details; but the very minimum you should know for bookmarks is that you can type not only letters to filter the list, but also arrows to navigate currently displayed list. And, if you're like me, you don't like to leave the home row, so, fzf
provides vim-like keybingins as well: Ctrl+j
behaves like down arrow, and Ctrl+k
behaves like up arrow.
Have fun!