147 comments

[ 9.7 ms ] story [ 209 ms ] thread
That looks clever and useful, I wasn't expecting that. I do maintain a few aliases to jump around, but this looks more efficient. Not sure though if I'll like "jump foo" better than simply "foo" that I use now, but maintaining those aliases is a pita.
cool! I'm a command-line junkie as well. So, I've been doing something similar for quite a while.

Here's what I have done :

    function ccb ()
    {
        FOO=`pwd`;    
        echo >> ~/shortcuts.sh;    
        echo alias $1=\'pushd $FOO\' >> ~/shortcuts.sh;    
        echo >> ~/shortcuts.sh;    
        . ~/shortcuts.sh    
    }
simple but quite effective.

Now, all I have to do is type ccb and give a shortcut name to register a shortcut. Then whenever i want to jump to that directory, I just have to type the shortcut.

Eg :

    foo@bar deeply-nested-dir# ccb deepd
    foo@bar deeply-nested-dir# cd
    foo@bar ~# deepd
    foo@bar deeply-nested-dir#
My method lacks the a) delete shortcut and b) list shortcut features that the above solution gives, though.
HN uses markdown, stick 4 spaces before each line in your code blocks to get them to indent properly, and drop the <br>

    function ccb ()
    { 
        FOO=`pwd`;
        echo >> ~/shortcuts.sh; 
        echo alias $1=\'pushd $FOO\' >> ~/shortcuts.sh;
        echo >> ~/shortcuts.sh;
        . ~/shortcuts.sh
    }
thanks for letting me know -- I've edited my comment.
So interesting since I've been using pratically the SAME idea for over an year now :D Shows how we arrive at nice solutions independently.

PS: I assume you are calling shortcuts.sh in your .profile - that way aliases are available across sessions.

yes. I call shortcuts.sh from my .bashrc
You might want to check out z: https://github.com/rupa/z
z is so much more useful because it automatically learns which paths you use most often. There is no need to explicitly mark folders. It just works.
How does it differ from autojump?
I don't know autojump, but from a cursory look: z is written in pure shell, is a single file, no external dependencies. autojump requires python, thus likely has more features.

IMHO, just for jumping around directories, z is good enough.

+1 for z. It's the primary way I navigate between folders at the command line.
Thank you! Glad I read the comments first... seems like z is more powerful yet easier to use. Ended up installing that.
https://github.com/clvv/fasd

I used to use z, but I've since switched to fasd. It's much like z, but also so much better. In addition to being able to do "z <part of directory>" you can do "f <part of file>". So, for example, if I've got a rails project I've worked on a lot recently I know its config.ru is high on frecency, so I'll just do "vim `f config`" to edit that file. If I want a file that's not so recent I can always do "vim `f -i config`" to pick from a list of files.

fasd is leaps and bounds above z in functionality, and I've thoroughly enjoyed using it.

Thanks for this. I've been using z and like it quite a bit, but I'm always open to ways to improve my workflow.
If you alias `v` to `f -e vim` then you can `v config` and `v -i config`.
I put z and fasd through their paces, and out of the box z works very intuitively. I like it. fasd takes a little more to get used to, but I'm going to put it through a full try out. Let's see if the additional features make it worth the while. In either case, thanks all for a great thread.
fasd is z on steroids. Fantastic tool.
Or you might just use ZSH (particularly in combination with oh-my-zsh), keep the marks around as zsh vars and skip all the symbolic linkage.
this way they would all be lost at the end of each session, and would not be shared across concurrent sessions, no?
I also do something similar but slightly different: https://github.com/roobert/dotfiles/blob/master/.zsh/robs/fu...

my method involves storing the bookmarks in a file but loading them into zshs directory hash. This means the directories are tab completable and if you have AUTO_CD set then you can change directory with simply: ~dir_name, otherwise: cd ~dir_name.

And with CDABLE_VARS set, you can simply do: dir_name In fact, you don't even have to use the directory hash for this, you just do, e.g.: MY_DIR=/path/to/dir; MY_DIR

Frankly, this is far superior to the linked method.

Except tab completion is annoying since zsh will include commands and environment variables in its guesses. The linked method simply provides a way to store the directory hash across sessions.
I had to modify Jeroen's code to get the `marks` shortcut working under Mac OS 10.8:

    export MARKPATH=$HOME/.marks
    function jump {
        cd -P $MARKPATH/$1 2> /dev/null || echo "No such mark: $1"
    }
    function mark {
        mkdir -p $MARKPATH; ln -s $(pwd) $MARKPATH/$1
    }
    function unmark {
        rm -i $MARKPATH/$1
    }
    function marks {
        ls -l $MARKPATH | sed 's/  / /g' | cut -d' ' -f9- && echo
    }
Here is another version of 'marks' for Mac OS that aligns the arrows. It also works when 'ls' is aliased.

    function marks {
        \ls -l $MARKPATH | tail -n +2 | sed 's/  / /g' | cut -d' ' -f9- | awk -F ' -> ' '{printf "%-10s -> %s\n", $1, $2}'
    }
If you are on a system with BSD stat like OS X you can get the same with

  function marks {
    (cd $MARKPATH && stat -f"%-10SN%SY" *)
  }
Even better with column

  (t="$(printf "\t")"; cd $MARKPATH && stat -f"%N$t%SY" * | column -ts"$t")
One more fix... if you have group names with spaces this will throw the field number off and you'll see the modification time preceding the mark name.

Adding '-n' to the ls command works around this by causing the group id to be printed instead of the group name and all works, i.e.: \ls -ln "$MARKPATH" | tail -n +2 | sed 's/ / /g' | cut -d' ' -f9- | awk -F ' -> ' '{printf "%-10s -> %s\n", $1, $2}'

And here's a completion code that works for Mac OS:

  function _jump {
      local cur=${COMP_WORDS[COMP_CWORD]}
      local marks=$(find $MARKPATH -type l | awk -F '/' '{print $NF}')
      COMPREPLY=($(compgen -W '${marks[@]}' -- "$cur"))
      return 0
    }
    complete -o default -o nospace -F _jump jump
Shouldn't this be

  complete -o default -o nospace -F _jump jump unmark
and then it simply replaces the existing _completemarks(), instead of needing both?
Thanks! Also, since alot of OSX paths contain whitespaces, I had to modify the mark function like so:

  function mark {
      mkdir -p $MARKPATH; ln -s "$(pwd)" $MARKPATH/$1
    }
Disclaimer: I'm not very good at bash, perhaps there is a nicer solution to this problem?
I went with:

  function marks {
      ls -l $MARKPATH | sed -E 's/ +/ /g' | cut -d' ' -f9- | sed -E 's/ -/     -/g' && echo
  }
where the "\t" (looks like a bunch of spaces in the second sed statement) was typed in using [Ctrl] + [V] and then [Tab].
A handy alias I use:

    alias ..='cd ..'
Moving up the file tree has never been easier!
Put the following in your .bashrc instead:

    shopt -s autocd
If you type a directory in the command-line, it’ll cd into it, e.g.:

    $ ..   # cd ..
    $ ~    # cd ~
    $ foo  # cd foo
I can’t live without this.
I believe this is set by default in oh-my-zsh as well.
In Zsh, you can set it so that specifying a bare directory goes to the directory as well, for any directory. Just do

  setopt autocd

    brew install fasd
Thank me later
You can also use ranger [1] to change directories, especially if you want to explore the directory tree quickly when you don't know exactly where to jump to. Install it and add the following to ~/.bashrc:

     function ranger-cd {
       tempfile='/tmp/chosendir'
       /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
       test -f "$tempfile" &&
       if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
         cd -- "$(cat "$tempfile")"
       fi
       rm -f -- "$tempfile"
     }

     # This binds Ctrl-O to ranger-cd:
     bind '"\C-o":"ranger-cd\C-m"'
(This script comes from the man page for ranger(1).)

Edit: Modified the above script for use with zsh (and multiple users):

     ranger-cd() {
       tempfile=$(mktemp)
       ranger --choosedir="$tempfile" "${@:-$(pwd)}" < $TTY
       test -f "$tempfile" &&
       if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
         cd -- "$(cat "$tempfile")"
       fi
       rm -f -- "$tempfile"
     }

     # This binds Ctrl-O to ranger-cd:
     zle -N ranger-cd
     bindkey '^o' ranger-cd
[1] http://ranger.nongnu.org/.
thanks! I tried: apt-get install ranger

it says it's not available. any idea why ranger is not longer available in package manager?

What is your distribution? I've just tried it and it's available in both Ubuntu 12.04 and Debian 7. The launchpad page says that it should be in later Ubuntu releases, too. Enable the "universe" repository then run apt-get update and see if it fixes the problem.
Good idea, but you shouldn't overwrite ctrl-O, which is a wildly useful function in bash: run current command in history and retrieve the next. If you want to re-run a sequence of several history commands, you can find the first in history (such as via ctrl-R reverse-isearch), then repeatedly hit ctrl-O to run it and retrieve the next command from history.
i'm using fasd a lot but this is absolutely awesome, thank you!
Thanks! I was already using ranger and this fits nicely with the i3 ethos as well.
Interesting idea. I've the bash built-in "pushd" and "popd" for a long time for similar reasons, but those effectively limit you to treating the history as a stack rather than an arbitrary list.
yeah, it can be annoying that the directory stack sequence keeps changing, which means that sometimes you can't easily re-run commands from your history if you've pushd since you last ran the command

e.g., a command like "tar cfz bak.tgz ~3 ~2/subdirectory ~1" would mean something else once you've "pushd +3".

I always find it hard to undo my sequence changes to get the stack in the same order as before, so usually end up typing "dirs -v" a lot (or its alias) and renumbering my ~N references.

I frequent systems where tcsh, for historical reasons, is the default shell, and for all its faults it does have some nice pushd settings that make directory browsing with 'cd +<number>' and 'dirs' very convenient.

I have ported this functionality to Bash: http://thrysoee.dk/pushd/

I found this is a useful addition to his scripts... I can't live without my tab completion!

    _jump()
    {
        local cur=${COMP_WORDS[COMP_CWORD]}
        COMPREPLY=( $(compgen -W "$( ls $MARKPATH )" -- $cur) )
    }
    complete -F _jump jump
Cleaner than the other variants here that use "find".
To have completion with bash you can use this:

  function _jump {
      local cur=${COMP_WORDS[COMP_CWORD]}
      local marks=$(find $MARKPATH -type l -printf "%f\n")
      COMPREPLY=($(compgen -W '${marks[@]}' -- "$cur"))
      return 0
  }
  complete -o default -o nospace -F _jump jump
Bash completion really needs better docs :-|
I was in the process of doing this myself and though "I wonder if it's already been posted in the comments". Thank you!
I just signed in to post almost exactly this function!
Cool, simple but very useful!

Bonus: if you want autocompletion for the jump and unmark commands, just add the following lines (in zsh):

    function _marks {
      reply=($(ls $MARKPATH))
    }
    compctl -K _marks jump
    compctl -K _marks unmark
I usually just alias directories I frequent in .bashrc... Easy enough, I guess.
I'll do it all the time in my local shell session, along the following:

  # I make a point to always have the trailing slash,
  # it makes life easier.
  # Also, tab-complete works with this in Zsh.
  a=/some/really/long/path/that/I/dont/like/typing/
  b=/some/other/really/long/path/too/
  cd $a
  # ... do stuff ...
  cp one two three $b
  cd $b
  # ... do more stuff ...
  cp four five six $a
  cd $b/even/longer/path
And so forth. Since it is a normal variable, you can use it anywhere you need the path.
I have a similar tool but it also manages shell environment variables, allowing you to jump around and/or switch working projects. You can check it out at: http://github.com/timtadh/swork
Without having me dig through the code, could you tell me in a few lines how it works? (not usage, but how internally you managed to handle shell environment vars, "clean after" finishing/switching, etc)
Cool!, I usually want to open a new terminal window and move to the same directory I am working in, I use these handy aliases :

alias here='pwd | pbcopy'

alias there='cd $(pbpaste)'

So now type `here` in current terminal window and type `there` in new terminal window to move to same directory!

[This works on Mac on other OS you could use xcopy or equivalent clipboard copy program]

This reminds me of NCD (Norton Change Directory) which then went on to inspire many tools, like the excellent KCD and WCD. These tools index rather than create symlinks, though.
Reminds me of z, which I use all the time. Anyone else use z? If you have not heard of it, get it now https://github.com/rupa/z.

z is a wonderful complement to cd. After cd into a folders with z set up, a file stores all folders navigated to sorted by frecency, then simply jump to a folder with z [foldernameregex].

z is absolutely wonderful. Even my boss has become a convert, and he's been hacking unix since the 1970s, when he worked at AT&T, and is very set in his ways.

[Disclaimer: rupa's a good friend of mine]

I've converted a few people to the z side. And yes, it is wonderful.

[Disclaimer: rupa is a good friend of mine, as well]

I was pretty excited about original link in this HN submission, but now I'm roughly twice as excited to learn about z. Love it.

[Disclaimer: I don't know rupa at all]

I have developed a very similar scheme for myself independently (haven't heard of z before, but thanks). A further idea is another command <code>e</code>, which is used like: <code>e reg1 reg2...</code>, and it will query z's database to see if there is a file in z's directory which matches the regs, and if found, open that file in Emacs. A similar command <code>f</code> will only print the path, so that <code>firefox $(f reg1 reg2)</code> will open the found file using firefox.
Here is a fully functional (albeit minimal) Windows version: http://appleseedhq.net/stuff/marks-v1.zip. Tested on Windows 7 (does not require PowerShell).

GitHub: https://github.com/dictoon/marks

Edit: to install, unzip anywhere and add to your path. Then:

  C:\Windows> mark win

  C:\Windows> marks
  win => C:\Windows

  C:\Windows> cd ..

  C:\> jump win

  C:\Windows> 
Marks will be stored in a marks\ subdirectory (relatively to the mark.bat file).
Thanks alot. Works on WIn8
Great! It's a smart idea just to store locations in simple text files. Thank you.
Clever!

With fish 2.0, the shell stores a list of "cd" commands issued from a specific directory, so mostly just typing "cd" yields an auto-completion hint out of the box.