Open Browser Bookmarks from the Terminal
Ever wanted to open your browser bookmarks from the terminal? Maybe not, but if you have a lot of bookmarks and you also happen to spend a lot of time in the terminal, this can be a great way to quickly launch your bookmarks straight from the terminal.
The bash script for doing this is actually fairly beefy, but I’ve broken into two parts:
- A script that parses browser bookmarks into a normalized JSON format.
- A script that takes the normalized bookmarks and manages the fuzzy-finding and opening of URLs.
The main reason for splitting the script into these two parts is so that it can support multiple browsers. All it takes to support a different browser is to create a shell script for that browser that parses into the normalized structure, then plug that into the main script and viola.
Normalizing Chrome bookmarks
Let’s get started with the script to normalize the Chrome bookmarks into a
structure that our main script can read. We are using
jq
extensively in this entire process
including most importantly parsing the Chrome bookmarks.
I won’t explain every detail, but in essence we are collapsing the recursive nature of Chrome bookmarks into a flat list and creating a slug for each entry (e.g. “My bookmark” becomes “my-bookmark”).
#!/usr/bin/env bashpath="$HOME/Library/Application Support/Google/Chrome/Default/Bookmarks"jq '.roots | to_entries[].value | recurse(.children[]?) | select(.type == "url") | { name, url, slug: .name | gsub("'\''"; "") | gsub("[^a-zA-Z0-9]"; "-") | gsub("-{2,}"; "-") | gsub("(^-|-$)"; "") | ascii_downcase } ' "$path"
The main script
Now that we have the script to read and normalize our Chrome bookmarks, we can
create our main script to fuzzy-find and open our bookmarks. If called with an
argument, the script will filter the results based on which results start with
the given string and either open fzf
if
there are multiple results, or open the bookmark if it is an exact match.
#!/usr/bin/env bashlist_bookmarks() { chrome-bookmarks}filter_bookmarks() { jq -c --arg key "$1" --arg value "$2" 'select(.[$key] | startswith($value))'}find_bookmark() { jq -c --arg key "$1" --arg value "$2" 'select(.[$key] == $value)'}# If bm is called without argument, show all bookmarksslug=${1:-}# Print raw JSON when requested for completionsif [[ "$slug" == '--json' ]]; then list_bookmarks | jq -cM exit 0fi# Filter the bookmarks by the provided slugbookmarks=$(list_bookmarks | filter_bookmarks 'slug' "$slug")# If no bookmarks matched the query, exit earlyif [[ -z "$bookmarks" ]]; then echo 'No bookmark found matching the provided slug' exit 1fi# If the slug matches an item exactly, open it without prompting to choose other# options.match=$(echo "$bookmarks" | find_bookmark 'slug' "$slug")if [[ -n "$match" ]]; then echo "$match" | jq '.url' | xargs open exit 0fi# Get the exact name of the bookmarkname=$(echo -e "$bookmarks" | jq -r '.name' | fzf --no-info)# Find the bookmark by name and open the URL in Chrome. If no bookmark is# selected from the list, exit with an error.if [[ -n "$name" ]]; then echo "$bookmarks" | find_bookmark 'name' "$name" | jq '.url' | xargs openelse exit 1fi
Here are some examples of how we might call our script in our terminal:
bmbm my-bookmark
Support for other browsers
As I mentioned, supporting other browsers is fairly trivial, one simply needs to create a shell script for the desired browser. I’ve created a version for the Arc browser which you can view in my dotfiles.
Shell completions
The script is setup in a way that makes shell completions very simple to add.
Simply add the --json
flag along with jq
to format the results in a way that
works for your shell’s completion mechanism.
Here is an example for the Fish shell:
function __bm_complete_bookmarks bm --json | jq -r '.slug'endcomplete -c bm -f -n __fish_use_subcommand -a '(__bm_complete_bookmarks)'