44

Is there something like bash's reverse-search-history (Ctrl-R), but for only directories?

I have some deep folder hierarchies that I want to jump to, so I'd like to use something like reverse-search-history, but it only looks for folder names and gives me absolute paths.

Essentially, it would give similar results to using !? but only matching commands with cd in the front, you can step through results, and full paths.

So far, the best solution I've found is bashmarks.

1
  • 1
    I recently started using z and that seems to work so far, but I used it for a few paths.
    – Rob
    Jun 20, 2011 at 17:04

15 Answers 15

16

Have a look at autojump:

One of the most used shell commands is “cd”. A quick survey among my friends revealed that between 10 and 20% of all commands they type are actually cd commands! Unfortunately, jumping from one part of your system to another with cd requires you to enter almost the full path, which isn’t very practical and requires a lot of keystrokes.

autojump is a faster way to navigate your filesystem. It works by maintaining a database of the directories you use the most from the command line. The jumpstat command shows you the current contents of the database. You need to work a little bit before the database becomes usable. Once your database is reasonably complete, you can “jump” to a commonly "cd"ed directory by typing:
j dirspec

1
11

There is

cd -

that is "cd[space][hyphen]" command, which goes to the directory you were before, essentially a "history of depth 1". Repeated "cd -" switches back and forth between two directories.

Quoting man page:

The following operands shall be supported: [...]

When a [hyphen] is used as the operand, this shall be equivalent to the command:

      cd "$OLDPWD" && pwd

Unfortunately, I don't know of a real built-in directory history.

2
  • There is pushd dir popd pair which can store your history stack of directories. This is really helpful in scripts that operate in different folders.
    – Jakuje
    Oct 25, 2015 at 18:59
  • I know of pushd and popd, and it is also mentioned here already.
    – apurkrt
    Oct 25, 2015 at 21:19
7

You can build your own cd command with pushd, popd, dirs builtin commands.

Usage

  • cd -- ( list current history )

  • cd -num ( go to num directory )

  • cd - ( go to previous directory )


function cd () 
{ 
    local hnum=16;
    local new_dir index dir cnt;
    if ! [ $# -eq 0 ]; then
        if [[ $# -eq 2 && $1 = "--" ]]; then
            shift;
        else
            if ! { 
                [ $# -eq 1 ] && [[ $1 =~ ^(-[0-9]{,2}|-|--|[^-].*)$ ]]
            }; then
                builtin cd "$@";
                return;
            fi;
        fi;
    fi;
    [ "$1" = "--" ] && { 
        dirs -v;
        return
    };
    new_dir=${1:-$HOME};
    if [[ "$new_dir" =~ ^-[0-9]{,2}$ ]]; then
        index=${new_dir:1};
        if [ -z "$index" ]; then
            new_dir=$OLDPWD;
        else
            new_dir=$(dirs -l +$index) || return;
        fi;
    fi;
    pushd -- "$new_dir" > /dev/null || return;
    popd -n +$hnum &> /dev/null || true;
    new_dir=$PWD cnt=1;
    while dir=$(dirs -l +$cnt 2> /dev/null); do
        if [ "$dir" = "$new_dir" ]; then
            popd -n +$cnt > /dev/null;
            continue;
        fi;
        let cnt++;
    done
}
2
  • This is awesome! Thank you! Jun 28, 2021 at 17:46
  • Where do you put it? ~/.bashrc? EDIT: OK, I tried and it works, quite nice
    – MikeKulls
    Aug 30, 2022 at 6:39
5

bash has pushd/popd/dirs. I have this in my .bashrc to auto-push directories onto bash's stack.

#let cd also pushd directories into stack. Use popd to reverse stack
function cd ()
{
  if [ -e $1 ]; then 
    pushd $1 &> /dev/null   #dont display current stack 
  fi
}

Pop these using popd and display the stack using dirs

4

I have made a script that has similar functionality to oh-my-zsh's dirs -v command that works on bash. If you have ever use oh-my-zsh, you might have noticed that the directory history provided by the command dirs -v will be reset every time you exit the terminal. It won't happen if you use this script, however.

The functionality:

  • Show the list of 10 most recent used directories with d.

  • Jump to any directory in the list by typing the number of the directory in the list.

  • A directory path will be put at the top of the list every time you visit a directory.

1
  • Am using zsh & didn't realise there was dirs -v so would need a bash built-in. Btw I installed zsh without any framework and it looks like it is still available to me. Anyways: thanks! Mar 24, 2021 at 18:42
2

I have had good experience with z-jump It allows completion, although only for the final destination, not stepping through a path. It does show the full path upon tab completion however.

2

A pure Bash implementation for a persistent directory history menu:

#
# ~/.bash_profile
#

cdHistoryFile=~/.bash_cdhistory

cd() {
    builtin cd $@ && echo $PWD >> $cdHistoryFile
}

cdh() {
    local IFS=$'\n'
    local directories=()
    for line in `tac $cdHistoryFile`; do directories+=($line); done
    cnt=${#directories[@]}
    for ((i = 0; i < cnt - 1; i++)) {
        for ((j = i + 1; j < cnt; j++)) {
            [ "${directories[$i]}" == "${directories[$j]}" ] && unset directories[$j]
        }
    }
    directories=("${directories[@]:1:${1:-15}}")
    local select=0 subset=("${directories[@]}")
    until [ $select -eq 1 ]; do
        select dir in ${subset[@]}; do
            case $REPLY in
                q) select=1; break ;;
               \.) subset=("${directories[@]}"); break ;;
               .*) subset=($(printf '%s\n' "${subset[@]}" | egrep "${REPLY:1}")); break ;;
                *) cd $dir; select=1; break
            esac
        done
    done
}

Explanation:

cd() wraps the cd shell builtin with a function that writes directory changes to a file. cdh() displays a selection menu of the last n-number of unique directories (in descending order from most recently accessed).

Usage:

cdhistory: cdh [n]

    Options:
      n         Get the latest number of unique directories [default: 15]

    Selections:
      q         Quit
      .         Display the original list
      .<regex>  Filter the current list
      #         Change directory

Example:

[jdoe@localhost /] $ . ~/.bash_profile && cdh
 1) /home/jdoe/tmp/2/3/1
 2) /home/jdoe/tmp/3
 3) /home/jdoe/code/projects
 4) /home/jdoe/tmp/2
 5) /home/jdoe/code/projects/cloud/pipeline
 6) /home/jdoe/tmp/1
 7) /
 8) /etc
 9) /usr/etc
10) /usr/share/libtool
11) /home/jdoe/tmp
#? .tmp
1) /home/jdoe/tmp/2/3/1  3) /home/jdoe/tmp/2      5) /home/jdoe/tmp
2) /home/jdoe/tmp/3      4) /home/jdoe/tmp/1
#? .3
1) /home/jdoe/tmp/2/3/1
2) /home/jdoe/tmp/3
#? 2
[jdoe@localhost /home/jdoe/tmp/3] $ 
1
  • One thing missing is find-as-you type like Ctrl-r. Looks easy to integrate with fzf like bulletmark's solution. Also, bash_cdhistory seems to have unbounded growth, but could be easily truncated in cdh. I wonder if you could use uniq somehow to avoid the double for loop? Maybe awk.
    – idbrii
    Apr 22, 2022 at 17:47
1

Just to chime in with my own experience, I wrote a simple script to address this requirement some time ago, it overrides the builtin cd command with a simple function which appends the new directory location to a history file, a python script is then used to provide a bash interface which dynamically updates an ordered list of directories as you enter search terms, somewhat like the reverse command search of bash.

It's available on git-hub for those that are curious.

1
1

I'd like to recommend my ltcd for quick navigation through directory history:

https://github.com/dczhu/ltcd

cd demo gif

It provides the following features to make life easier:

  • Global dir listing, which shows recently visited dirs from all terminal tabs/windows.
  • Local dir listing, which is local to current shell session.
  • Both listings support quick navigation by using j/k (go down/up), numbers, and word searching.
  • Global free jumping (e.g. "cd dir" or "cd ar" to go to /path/to/foo/bar/directory/).
1

I'll suggest another solution: fuzzy path search

  • fzf

    After having it run in the background, you can press Alt+C to open a window where you can type a fuzzy string to find directories. After you select one and press Enter, fzf will change to that directory.

    fzf

  • fuzzy bash completion

  • Command-T:

    Command-T in action

See also

There are also various shells and shell extensions that support partial path autocomplete and fish is one of them

For example cd /v/l/fsck Tab will convert the path to /var/log/fsck/. cd /u/s/appl Tab will expand the path to /usr/share/applications/

1

I wrote my own cdhist tool for this about 15 years ago and have used it all day every day since. It replaces your cd command with an alias which keeps track of the directories you visit so that you can cd -- to list them and then select one. It also integrates with fzf if you prefer to fuzzy search over previously visited directories.

2
  • This looks very close to what I was looking for! One thing that's not clear from the readme is whether cd /usr/local/share ; cd ../bin will add /usr/local/bin or ../bin to the history. I assume the former?
    – idbrii
    May 3, 2021 at 18:47
  • 1
    Well you did two cd commands so it adds /usr/local/share to the history then adds /usr/local/bin to the history. I don't understand why you would think it would work any other way? BTW, it takes seconds to pip install this (or unstall it) so just try it out.
    – bulletmark
    May 4, 2021 at 21:15
0

Well, you could add this code snippet to your ~/.bashrc, which

  1. provides a custom cd command

        function cd ()
        {
            exists=false
            for dir in "${CDHIST[@]}"; do
                [ "$dir" == "$1" ] && {
                    exists == true
                    break
                }
            done
    
            $exists || {
                len=${#CDHIST[@]}
                ${CDHIST[$len]} = "$1"
            }
    
            builtin cd "$1"
        }
    
  2. and provides a cd history search command.

    function cdhist ()
    {
        #TODO: Make this magical.
    
        for dir in "${CDHIST[@]}"; do
            echo "$dir"
        done
    }
    

Of course, the cdhist command I provided is very basic, and not what you wanted; but it is conceivable to use case statements or parameter expansion to achieve something similar to what you want.

You could even add some "Programmable completion" function, which could be used to add the full cd /path/to/mydir command based on a unique sub-string of /path/to/unique/mydir, although that method would still require you to type cd unique/mydir<tab>.

2
  • 1
    I misread your code and found bash's dirs command which does essentially the same thing as my_dirs, but on one line.
    – idbrii
    Jun 20, 2011 at 18:55
  • 1
    Cool. So not only does bash support it; it implements it. I wish I had more time to study bash's deep magic. Of course, this only supports remembering pushd, not cd. But I suppose alias cd=pushd would be the most elegant form.
    – jpaugh
    Jun 25, 2011 at 0:46
0

Try WCD which is a cd replacement that stores the list of directories you've changed into. That's exactly a directory history list that you can view with any text editors

Running wcd foo will show a list of the directories containing foo and you can choose one of them to jump into

Wcd is a command-line program to change directory fast. It saves time typing at the keyboard. One needs to type only a part of a directory name and wcd will jump to it. Wcd has a fast selection method in case of multiple matches and allows aliasing and banning of directories. Wcd also includes a full screen interactive directory tree browser with speed search.

Wcd was modeled after Norton Change Directory (NCD). NCD appeared first in The Norton Utilities, Release 4, for DOS in 1987, published by Peter Norton. NCD was written by Brad Kingsbury.

Examples of wcd usage:

wcd Desk

will change to directory /home/waterlan/Desktop

But also

wcd *top

will do that.

0

here another implementation, history and also locate usage: https://github.com/joknarf/cdhist (compatible ksh / bash / zsh / ash shells linux* / macos / ish / msys2 / solaris implementation...)

cdhist in action

1
  • 1
    Avoid posting answers to old questions that already got well received answers unless you have something substantial and new to add.
    – Toto
    Jun 24, 2023 at 22:27
-1

I put this tool together, which combines previous solutions for storing a comprehensive global CLI history with an interactive grepping tool called percol (mapped to C^R). I use it to retrieve commands by where they were run or to find a directory by a command. It's still slick on the first machine I started using it, now with a >2 year old CLI history.

The grep tool works on both path and command, but might still be useful to you. See also 'dirs -v' in zsh

https://github.com/gawells/ariadne

3
  • this stores a CLI history with path, command and date. It's not path-only, but still useful for reverse searching dir history (bash and zsh) Aug 20, 2017 at 14:02
  • I think you forgot to link to your tool. From your description, I'd suspect it is deficient in that commands only work from where they're invoked. So cd share/man only works if I'm already in /usr.
    – idbrii
    Sep 26, 2017 at 16:09
  • oops! my bad, I thought I'd put the url in the text, didn't notice something went wrong Oct 27, 2017 at 14:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .