Hacker News new | comments | show | ask | jobs | submit login
Quickly navigate your filesystem from the command line (jeroenjanssens.com)
457 points by jeroenjanssens on Aug 17, 2013 | hide | past | web | favorite | 144 comments

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].

I set my version up with a -f on the unmark rm so that I don't need to be prompted to remove it. Also added some basic completion for both jump and unmark

  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 -if $MARKPATH/$1 
  function marks {
      ls -l $MARKPATH | sed 's/  / /g' | cut -d' ' -f9- | sed 's/ -/\t-/g' && echo

  _completemarks() {
      local curw=${COMP_WORDS[COMP_CWORD]}
      local wordlist=$(find $MARKPATH -type l -printf "%f\n")
      COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
      return 0

  complete -F _completemarks jump unmark

The purpose of the -i flag is to prompt the user before deleting. Mixing the -i and -f flags is a little moot.

Try removing the -i option to obtain what you want.

Didn't catch that, it was a quick hack of an edit, thanks.

I recommend avoiding -f on rm if you can. Make one mistake and it can be painful. In this case think removing the -i from the unmark function will accomplish what you want.

I made something to the OP's system for the same reason of disdain for super deep trees at work. I'll make a gist and edit this post with it in a few minutes.

Edit: https://gist.github.com/desertmonad/6258429

I like the OP's idea of using symbolic links. That makes it more generally useful (i.e. a gui app can use those simple paths too). I may switch over to that.

Why not use unlink?

What's the advantage?

Nice job! I had to modify _completemarks() function though to work on OSX.

  _completemarks() {
    local curw=${COMP_WORDS[COMP_CWORD]}
    local wordlist=$(find $MARKPATH -type l -exec basename {} \;)
    COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
    return 0

nice additions. For OSX I had to modify the local wordlist part of the auto complete function to read:

local wordlist=$(ls $MARKPATH)

The variable expansions really ought to be quoted, like this:

  function unmark {
      rm -if "$MARKPATH/$1"
Otherwise you are asking for much sadness.

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 :-|

Hmmm, this doesn't seem to work in `zsh`. Typing `jump <tab>` suggests everything in the current directory (folders and files).

Does anyone here know offhand how autocompletion in `zsh` works?

see my comment at the bottom https://news.ycombinator.com/item?id=6229468

Yeah, I'm a bash user.

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!

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.

For Windows users I have a closely related shameless plug:

cdhere navigates your cmd.exe to wherever the current topmost Explorer window is pointed at.

It's not the same as the OP's trick, of course, but since you're voluntarily using Windows, I'm going to assume you "live" on the command line less than the average UNIX user, and more on GUI tools such as good old Explorer.


What many people don't seem to know is that the location bar in Explorer (at least on Windows 8) can act as a run dialog, and it supplies the launched program with the current folder as a parameter. So simply hit Ctrl+L and type "cmd" or "powershell" or whatever command from your PATH and hit enter.

Amazing tip!

FYI: On Windows 7, you need to press Alt+D to get to the location bar. (I believe that's the case for XP as well.) Ctrl+L doesn't seem to do anything.

Alt+D is my preferred shortcut anyway, since it has the same effect in e.g. fullscreen IE10. Consistency is nice.

Totally. Also works in Chrome and Firefox.

Thanks for the tip!

Neither Ctrl+L nor Alt+D work for me (Win7 Ultimate, 64bit), but even with clicking manually this is extremely useful for me.

I have been using F6 in my browsers, and it does seem to focus the address bar in File Explorer too. You need to press Enter first to be able to type commands though.

I'm not sure voluntary is necessarily true. I have had to use Windows several times because we were working with Windows only programs.

On a different note, back in UNIX land I found myself doing the opposite of what you described: opening the file explorer to the same directory as the command-line. Of course, there is not much to that trick other than choosing an explorer that opens to the working directory or a path provided as an arguement.

I like this.

On Windows you can also get a nice "browse for folder" dialog in batch files with Wfolder [1]. If you replace "cdwhere.exe" in your script (cdhere.cmd) with "wfolder.exe" [2] and put wfolder.exe in the same directory as cdhere.cmd then instead of going to the location open in the topmost Explorer window the "cdhere" command will ask you where to cd, starting at your current location (like so: http://i.imgur.com/3MCzD4b.png).

Edit: Wfolder comes with a script that's analogous to cdhere.cmd.

[1] http://www.horstmuc.de/wcon.htm

[2] I did it in http://pastebin.com/aq0FiKmz.

You can hold shift and right-click in Explorer and select "Open Command Window Here" (at least in Win 7 and 8).

Or even faster: shift + context menu button, then W.

About the same speed on Windows 8: File -> Open Command Prompt.

Yep, this works fine, until you did it a 7th time and end up with a task bar full of unused console windows.

If you want to reuse an existing cmd window you can type in "cd " then drag/drop the directory from any Explorer shell. You could also close the shell after finishing with it.

Not trying to take anything away from your script, it has a use-case, but just pointing out that there are alternate solutions for systems that don't have your script installed.

Another useful windows trick: to open a new file explorer window at the pwd of the command prompt, type 'explorer .'.

or slightly shorter: 'start .'

I used to have a really hacky AppleScript to do the same thing in OS X. The crappy part was that launching the whole AppleScript runtime was really slow, but that was the only API I could find with getting that information from the Finder.

You can also drag any file or folder onto your terminal, which will paste the absolute filepath at your cursor point.

Yeah, but I rarely use the mouse when using either a file browser or the terminal, so switching to it is less convenient than a short terminal command.

You could install cygwin and use the OP technique, though.

unfortunately cygwin takes a lot longer to start than cmd, so I use it only occasionally. are there any fixes for that maybe? :)

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 {
       /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
       test -f "$tempfile" &&
       if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
         cd -- "$(cat "$tempfile")"
       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() {
       ranger --choosedir="$tempfile" "${@:-$(pwd)}" < $TTY
       test -f "$tempfile" &&
       if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
         cd -- "$(cat "$tempfile")"
       rm -f -- "$tempfile"

     # This binds Ctrl-O to ranger-cd:
     zle -N ranger-cd
     bindkey '^o' ranger-cd
[1] http://ranger.nongnu.org/.

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.

Thanks! I was already using ranger and this fits nicely with the i3 ethos as well.

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.

Debian "Squeeze" 6.0.7 x32

Looks like it simply never was in Squeeze. Install python2.6 and get the latest stable release from http://nongnu.org/ranger/ranger-stable.tar.gz instead.

i'm using fasd a lot but this is absolutely awesome, thank you!

You might want to check out z: https://github.com/rupa/z


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.

If you alias `v` to `f -e vim` then you can `v config` and `v -i config`.

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.

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.

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.

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

        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".

I've been using Bashmarks (https://github.com/huyng/bashmarks) for years, which appears to be basically the same thing but with a less verbose interface.

And poshmarks for those on Windows - https://github.com/manojlds/poshmarks

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

Marks will be stored in a marks\ subdirectory (relatively to the mark.bat file).

Great! It's a smart idea just to store locations in simple text files. Thank you.

Thanks alot. Works on WIn8


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.

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 ()
        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 ()
        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

This is really neat. I have added it to alias.sh for easy inclusion: http://alias.sh/filesystem-markers-jump

Here's a `fish 2` shell version, that works on the Mac. You can add it as `~/.config/fish/functions/mark.fish`:

    set MARKPATH $HOME/.marks
    function jump
      cd $MARKPATH/$argv; or echo "No such mark: $argv"
    function mark
      mkdir -p $MARKPATH; and ln -s (pwd) $MARKPATH/$argv
    function unmark
      rm -i $MARKPATH/$argv
    function marks
      ls -l $MARKPATH | cut -d' ' -f12-; and echo

-f9- for me in cut, but otherwise it works :). Thanks!

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 don't understand the advantage of using handwritten function instead of the shell builtins. The `mark` function could be replaced by `alias -g`, `jump` could be replaced by `cd` and `unmark` could be replaced by `unalias` :

    % cd ~/Dev/W3/m-r-r.github.io
    % alias -g :blog="$PWD"
    % jekyll build -d /tmp/out/ && cd /tmp/out
    % ./index.html
    % cd :blog
    % pwd
    % alias -g
In addition, this method works with any shell command:

    % pwd
    % make -C :blog publish
    % git clone git://git.complang.org/ragel.git `echo :c`/ragel
    % vim `echo :vim`/templates/\*.rl
On the other side, the aliases are not saved at exit unless you write a function to do it:

    % tail -n 15 ~/.zshrc
    function aliases {
            alias -L -r
            alias -L -g
            alias -L -s
        ) | uniq

    function save-aliases {
        aliases > ~/.zsh_aliases

    if [ -f ~/.zsh_aliases ]; then 
        . ~/.zsh_aliases

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've been using the following in some form for over 3 decades! (now in zsh)

    alias   .=cd
In /bin/sh "." sources a script. While this can be common in a script, one rarely does this interactively so I usurped . for the most common operation!

    alias   ,=pushd
    alias   ,,=popd
    alias   ,.=dirs
Think of cd as a goto, pushd as a call, popd as a return and dirs as a stack trace!

    ..() { cd ../$* }
    cdpath=(. .. ~/src ~)
Use of cdpath can be confusing so it is best to show part of $PWD in the prompt:

    PS1="%* %5. %h%(#.#.:) "
This ends the prompt with a # if you are running as superuser.

These aliases/functions are enabled in .zshrc (only if run interactively -- that is, they are included below the following test):

    if [[ ! -o interactive ]] ; then return; fi
The "benefit" of Jeroen's mark/unmark is that these paths persist (and can be used from any window running a shell. I have not found much need for that + I can have different $dirs in different windows. Also, given that my shell windows (under screen) persist for a very long time, I don't need symlink's persistence!

Alternatively I define "." to be a function that does arg specific operation (cd if a dir, acroread if .pdf, vi if text etc.).

This is great, but I don't see why this is better than autojump.

* Autojump automatically saves marks,

~~~ cd Downloads # And you are done. ~~~

* You don't have to think about mark names,

~~~ cd Projects/Python # It will be called `python`. ~~~

* You don't need to remember mark names,

~~~ j haskell # If you want `Projects/Haskell`, odds are the mark is auto-named `haskell`. ~~~

* You can specify only some part of mark name;

~~~ j bar # And you are in `Stuff/foobar`. ~~~

Other than that, this is, as I said before, great! Looking forward to go through the source code to learn more about Bash.

Why on Earth would you want to save every directory you change to?!

I noticed that 'mark' doesn't work if you have spaces in a directory name which you can't avoid on the Mac (I tried to mark an interior folder inside the iPhone Simulator which is inside "~/Library/Application Support/iPhone Simulator/". Wrapping in quotes seems to do the trick:

  function mark { 
       mkdir -p $MARKPATH; ln -s "$(pwd)" $MARKPATH/$1

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 is a very informative thread for people trying to streamline their daily shell experience. I'm looking forward to experimenting with the utilities mentioned here. I spend a significant amount of time in, and moving between CVS sandboxes. All my sandboxes have the same internal directory structure, but the root of the path changes. To speed up navigation time I created a tool to replace directory names with aliases. The aliases are persistent. They can also be "stacked", which allows you to specify a sandbox as an alias, then reuse your existing aliases to navigate within the sandbox. Auto-completion in the shell is an excellent companion as well. More information on "stacked.aliases", as well as the source can be found here: https://github.com/ttomaino/stacked_aliases/wiki. I include one example to illustrate:

As an example, the following two sandboxes contain the Broadcom Ethernet driver:

/build/username/sandbox_a/software/vendors/linux/linux- /build/username/sandbox_b/software/vendors/linux/linux-

Use the add alias command aa <alias name> <directory> to create aliases for the sandboxes:

aa a /build/username/sandbox_a aa b /build/username/sandbox_b

Then create an alias for the Broadcom directory:

aa brcm software/vendors/linux/linux-

To change to the Broadcom directory in sandbox_a, just stack the appropriate aliases using the ua command. Stacking is done by stringing the aliases together with a period delimiter:

ua a.brcm

To change to the Broadcom directory in sandbox_b:

ua b.brcm

Great idea!

I don’t want symlinks to be resolved (/var/www becomes /private/var/www on os x) so I modified it to store the path in the file:

  export MARKPATH=$HOME/.marks

  function jump {
    mark=$(head -n 1 "$MARKPATH/$1" 2>/dev/null)

    if [[ $mark != '' ]]; then
      cd $mark
      echo "No such mark: $1"

  function mark {
    mkdir -p "$MARKPATH"; echo "$(pwd)" > "$MARKPATH/$1"

  function unmark {
    rm -i "$MARKPATH/$1"

  function marks {

    find "$MARKPATH" -type f | while read filename
      printf "%-12s -> %s\n" $(basename ${filename}) $(head -n 1 ${filename})
I think the if syntax is zsh so probably won’t work with bash.

I just do this (in zsh):

    setopt autopushd pushdminus pushdsilent pushdtohome pushdignoredups
Which automatically pushes (unique) directories I've visted into $dirstack.

Then I have an alias:

    d () {
            local dir
            select dir in $dirstack
                    echo $dir
            test "x$dir" != x && cd $dir
That gives me a list of directories to jump to in my history:

    ~/src/git/emacs-prelude $ d
    1) /home/kelvie/tmp/typhoon          3) /home/kelvie/src/git              
    2) /home/kelvie/tmp                  4) /home/kelvie                      
So I just type 1-4 to go back to a directory I've previously been in.

I'm too lazy to type "jump" so I've modified it to automatically add a Bash alias for each one as well.


Unless I'm missing something you also need a function to create all aliases when the shell session starts?

That's what the for loop at the bottom does.


I have no idea if anyone else does this, but I use tmux a lot for different projects. I usually have vim in one tab, logs in another, and tests in another. However, each tab I open I need to cd into the same directory. As such I came up with this helper for zsh. Every time I change a directory it records it. When I start a new session (new tmux or iTerm tab) it goes straight to that directory:

    # record directory in ~/.lastpwd
    record_pwd() {
      pwd > ~/.lastpwd
    # go to last directory when opening a new shell
    if [[ -d `cat ~/.lastpwd` ]]; then
      cd `cat ~/.lastpwd`
For other shells that don't have something like chpwd_functions you could just alias cd.

> However, each tab I open I need to cd into the same directory.

As of tmux 1.7, this should be unnecessary. Every tab and split will open in the working directory of the current pane, and the behavior can be changed with the `default-path` option.

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

Very cool, thanks! Although:

On OS X: "-bash: shopt: autocd: invalid shell option name"

Apple and their outdated tools... It gets annoying after a while.

Thank you all for your kind words and helpful suggestions! I shall adapt both the post and the code accordingly.


I have just added (1) quotes to the code, (2) a section about tab completion, and (3) a note for Mac OS X users. Many thanks again; HN is a great community!


I've been doing a very similar thing for a few years: https://chris-lamb.co.uk/posts/optimising-directory-navigati...

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/

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.

"Like many others, I spend most of my day behind a computer."

I tend to spend my time in front of one. Is there an advantage to being behind it?

But jokes aside, I added this to my bashrc, looks useful.

Not sure if the author knows or not, but there exists a BASH environment variable called CDPATH which does the same thing, and is a lot easier to use.

Watching all the comments, I think the author should create a repo so people can fork or contribute instead of comment different versions :p

This is how I know I am old I create a symlinks in my home directory to oft used locations and get there with cd ~/linkname Has the added bonus of making my short path be ~/linkname rather than ~/some/very/deep/pocket/in/some/git/repo

Nice idea, reminds me of ncd from a far away, foggy past. I suggest some escaping on the directory name parameter though. If you're not careful, you're going to run into trouble with paths that have spaces or risk some nasty security issues with embedded special characters.

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.

I've add a new bash version based on Jeroen's code, it's more easier to use in a Bash environment.

Welcome to use, thanks for Jeroen's smart work.


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)

I used some of this code to implement a more orthodox utility using flags instead of different syntax for different commands.


It works great! Great blog post.

My Z-Shell predicts and auto-completes everything I do often perfectly, thus typing 2-3chars not only get's me deep into directories, but also do actions I need. But maybe the idea of folder bookmarks can still be useful, idk.

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.
  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've updated the completion code to work on ubuntu and os x:


Here is one that works with zsh: https://github.com/bufordtaylor/swiftcommandline

This does look very similar to the setup I am using (https://github.com/huyng/bashmarks)

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?

Caps insensitive auto-complete is pretty handy. Add 'set completion-ignore-case on' to '/etc/inputrc'.

I've been looking for a way to make this less annoying, hanks!

I use powershell aliases for this. Works great.

happy guaranteed https://github.com/clvv/fasd

    brew install fasd
Thank me later

when mark name contains space, the auto complete doesn't work, anyone have solutions?

Applications are open for YC Summer 2018

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact