My latest bash toy: a “cd” replacement

I just made this little cd function that is a drop-in replacement for the builtin bash cd command. The idea was to make it really easy and productive to navigate around to commonly used directories in bash without forcing someone to learn new habits like in the case of pushd, popd, and a bunch of other stuff like cdargs and gotodir.

cd ()
{
    if [ "$*" = "" -o "$*" = "-" ]; then
        builtin cd $*;
        return;
    fi;
    realpath=$(realpath $*);
    if [ -z "$realpath" -o ! -d "$realpath" ]; then
        realpath=$(realpath ~/recent_dirs/$* 2> /dev/null);
    fi;
    if builtin cd $realpath; then
        if [ "$*" = "" -o "$*" = "-" ]; then
            return;
        fi;
        ln -s $realpath ~/recent_dirs 2>/dev/null;
    else
        :;
    fi
}

4 thoughts on “My latest bash toy: a “cd” replacement

  1. Your solution is the nicest I’ve seen for the problem of directory bookmarks. However, I think the notion of history needs to be per-shell, not global across all shells. For directories, a $DIRSTACK-style working set is probably more useful than a strict browser-style history, so I extended your solution by adding:

    • support for pushd’s $DIRSTACK , the Nth entry of which is accessed via cd N
    • searching $DIRSTACK and the directory bookmark targets via regular expressions
    • fallback support for $CDPATH if the above fail
    • support for symlinks by prepending $PWD instead of using realpath
    • resolution of bookmark collisions in favor of more recent bookmark
    • not bookmarking directories when reached via short hops e.g. cd ..
    # cd [dir|N|pattern]
    #
    # if arg is a directory, then
    #    if arg has > 2 slashes, then
    #       bookmark dir and pushd there
    #    else
    #       cd there
    # else if arg is a number N
    #    cd to Nth entry of $DIRSTACK
    # else if arg is a substring in $DIRSTACK
    #    cd to first matching entry
    # else if arg is a substring in bookmarks
    #    cd to newest matching entry
    # else
    #    attempt to cd to arg using $CDPATH
    cd ()
    {
    	if [ "$*" = "" -o "$*" = "-" ]; then
    		builtin cd ${1+"$@"}
    		return
    	fi
    	local target
    	if [ -d "$*" ]; then
    		slashes=${1//[^\/]} # deletes all non-slashes
    		if [ ${#slashes} -gt 2 ]; then
    			pushdir "$1"
    			return
    		fi
    	fi
    	# if the non-numeric part of $1 is empty...
    	if [ -z "${1//[0-9]}" ]; then
    		# ...then go to Nth DIRSTACK entry
    		target=`builtin dirs -l +"$@" 2> /dev/null`
    	fi
    	if [ -z "$target" ]; then
    		target=`builtin dirs -p -l | grep -i "$1" | head -1`
    	fi
    	if [ -z "$target" ]; then
    		target=`dirz -t | grep -i "$1" | head -1`
    	fi
    	if [ -z "$target" ]; then
    		target="$1"
    	fi
    	if builtin cd $target; then : else
    		echo "Also no match in DIRSTACK or ~/dirs" > /dev/stderr
    	fi
    }
    
    # pushdir dir - pushd dir after bookmarking via a symlink in ~/dirs
    pushdir ()
    {
    	local dest="$1"
    	# if relative, prepend PWD
    	if [ "${dest:0:1}" != "/" ]; then
    		dest="${PWD}/${dest}"
    	fi
    	# ln confused by trailing slash, so delete
    	dest=${dest%/}
    	ln -sf "$dest" ~/dirs
    	builtin pushd "$dest" > /dev/null
    }
    
    # dirz [-t] - list bookmarked dirs sorted [by time]
    dirz ()
    {
    	local ordercmd=sort
    	if [ "$1" == "-t" ]; then ordercmd=cat; fi
    	/bin/ls -lt ~/dirs | tail +2 | sed 's/.* -> //' | $ordercmd
    }
    
  2. Hmm, some kind of markup system is in effect here, but WordPress doesn’t offer preview or convenient compose help. Marc can see the code in ~holtz/.bash_function; others may be out of luck. 🙂

  3. I did a little bit of cleanup in your comment to get around some of the automatic WordPress formatting, but I think it’s still getting a bit screwed up. Maybe I can just upload a copy as a plain text file to my site.

  4. Nice… how about some directions on using it? For example, when I try to cd into a dir now, I get “bash: realpath: command not found” — are there dependencies you might want to tell the rest of us how to fulfill?

    Thanks!
    -jq

Leave a Reply

Your email address will not be published. Required fields are marked *