Bash cd bookmark in 20 lines
Tags: bash, linux, productivity, beginners
Published: 2020-01-20 17:41:00

---
title: Bash cd bookmark in 20 lines
published: true
description: Tired to switch directories in the shell ? A simple bookmark will help.
tags: bash, linux, productivity, beginners
---

This is a small bash script, save it as `m.sh`:

```bash
# bash directory bookmark
function cd_mark() {
    MARKPATH="${MARKPATH:-$HOME/.local/share/marks}"
    [ -d "$MARKPATH" ] || mkdir -p -m 700 "$MARKPATH" 2> /dev/null
    case "$1" in
        +*)            # m +foo  - add new bookmark for $PWD
            ln -snf "$(pwd)" "$MARKPATH/${1:1}" 
            ;;
        -*)            # m -foo  - delete a bookmark named "foo"
            rm -i "$MARKPATH/${1:1}" 
            ;;
        /*)            # m /bar  - search bookmarks matching "bar"
            find "$MARKPATH" -type l -name "*${1:1}*" | \
                awk -F "/" '{print $NF}' | MARKPATH="$MARKPATH" xargs -I'{}'\
                sh -c 'echo "{} ->" $(readlink "$MARKPATH/{}")'
            ;;
        "")            # m       - list all bookmarks
            command ls -1 "$MARKPATH/" | MARKPATH="$MARKPATH" xargs -I'{}' \
                sh -c 'echo "{} ->" $(readlink "$MARKPATH/{}")'
            ;;
        *)             # m foo   - cd to the bookmark directory
            local dest="$(readlink "$MARKPATH/$1" 2> /dev/null)"
            [ -d "$dest" ] && cd "$dest" || echo "No such mark: $1"
            ;;
    esac
}

# by default, alias cd_mark to m
alias ${MARKCMD:-m}='cd_mark'
```

Then put a single line in your `.bashrc` or `.zshrc`:

    source /path/to/m.sh

Or, you can simply copy & paste the content to your `.bashrc` or `.zshrc`.

Now you can have your `m` command to manipulate the directory bookmarks:

- `m +foo`: add a new bookmark for your current working directory
- `m -foo`: delete a bookmark
- `m foo`: cd to the given bookmark directory
- `m /bar`: search bookmarks matching "bar"
- `m`: print a list of all bookmarks

`cd` to your most recently accessed directories and use `m +foo` to create a bookmark named `foo`, and use `m foo` to go back at anytime.

Let's polish it a little, for example, add a completion function for bash/zsh:

```bash
if [ -n "$BASH_VERSION" ]; then
    function _cdmark_complete() {
        local MARKPATH="${MARKPATH:-$HOME/.local/share/marks}"
        local curword="${COMP_WORDS[COMP_CWORD]}"
        if [[ "$curword" == "-"* ]]; then
            COMPREPLY=($(find "$MARKPATH" -type l -name "${curword:1}*" \
                2> /dev/null | awk -F "/" '{print "-"$NF}'))
        else
            COMPREPLY=($(find "$MARKPATH" -type l -name "${curword}*" \
                2> /dev/null | awk -F "/" '{print $NF}'))
        fi
    }
    complete -F _cdmark_complete ${MARKCMD:-m}
elif [ -n "$ZSH_VERSION" ]; then
    function _cdmark_complete() {
        local MARKPATH="${MARKPATH:-$HOME/.local/share/marks}"
        if [[ "${1}${2}" == "-"* ]]; then
            reply=($(command ls -1 "$MARKPATH" 2> /dev/null | \
                awk '{print "-"$0}'))
        else
            reply=($(command ls -1 "$MARKPATH" 2> /dev/null))
        fi
    }
    compctl -K _cdmark_complete cd_mark
fi

```

After that, when you press tab after `m` command, you can get suggestions from the completion function.