DEV Community

Linwei
Linwei

Posted on

Bash cd bookmark in 20 lines

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

# 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'
Enter fullscreen mode Exit fullscreen mode

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

source /path/to/m.sh
Enter fullscreen mode Exit fullscreen mode

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:

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

Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)