cd -
can move to the last visited directory. Can we visit more history other than the last one?

- 93,103
- 40
- 240
- 233

- 101,790
-
Meta discussion about this question's tags – Gilles 'SO- stop being evil' Jan 25 '15 at 23:20
15 Answers
You didn't specify which shell you are using, so let this be excuse to advertise zsh.
Yes, we do have more history for cd
, namely cd -2
, cd -4
etc. Very convenient is cd -
TAB, especially with completion system and colors enabled:
This is what I have in .zshrc:
setopt AUTO_PUSHD # pushes the old directory onto the stack
setopt PUSHD_MINUS # exchange the meanings of '+' and '-'
setopt CDABLE_VARS # expand the expression (allows 'cd -2/tmp')
autoload -U compinit && compinit # load + start completion
zstyle ':completion:*:directory-stack' list-colors '=(#b) #([0-9]#)*( *)==95=38;5;12'
And the result:

- 47,140
-
5
-
12
-
30Unless the question specifically asks about
bash
, this is a valid answer. Don't remove it. – liori Sep 26 '14 at 17:21 -
What if the OP edits his question to include bash only, will this answer still be valid? – Ooker Sep 26 '14 at 21:57
-
2
-
@Ooker that's discussed in detail somewhere on meta.SO or meta.SE, but in this case I would say yes because it's still very useful, and even worthwhile to say "this feature could be implemented because it exists here." – djechlin Sep 28 '14 at 02:03
-
4It might bear mentioning that apart from
setopt AUTO_PUSHD
, none of the above setup is required to get an omnipresent directory stack with completion in stock zsh.PUSHD_MINUS
reverses the sense ofcd +
andcd -
(a matter of taste),CDABLE_VARS
is irrelevant to directory stacks, and thezstyle
invocation given here simply adds colouring to the output of a directory stack completion. One must however initialise the completion subsystem withautoload -U compinit && compinit
. – wjv Apr 07 '16 at 15:59 -
1Great! I didn't know about this specific feature of zsh! Thanks a lot for the hint! +1 – rmbianchi Nov 26 '20 at 17:15
-
Thanks @jimmy, for not deleting that answer. Nearly a decade later I found it very useful. – undg Dec 01 '22 at 10:41
The command you are looking for is pushd
and popd
.
You could view a practical working example of pushd
and popd
from here.
mkdir /tmp/dir1
mkdir /tmp/dir2
mkdir /tmp/dir3
mkdir /tmp/dir4
cd /tmp/dir1
pushd .
cd /tmp/dir2
pushd .
cd /tmp/dir3
pushd .
cd /tmp/dir4
pushd .
dirs
/tmp/dir4 /tmp/dir4 /tmp/dir3 /tmp/dir2 /tmp/dir1

- 39,297
-
1There's also
$OLDPWD
in case you want to flip back and forth between two directories using the same command, but I'm unsure how shell-specific and distribution/kernel-specific this is. – mechalynx Sep 26 '14 at 18:07 -
4@ivy_lynx
OLDPWD
exists in all POSIX shells, but it's useless for this question which asks how to go beyond that (the question already mentionscd -
which is a shortcut forcd "$OLDPWD"
). – Gilles 'SO- stop being evil' Sep 26 '14 at 23:18 -
2Is there a reason you use
cd /tmp/dir1; pushd .
instead of justpushd /tmp/dir1
? – GnP Oct 03 '14 at 00:23 -
@gnp, no specific reason. It was just taken from the link that I have referred to in the answer.
pushd /tmp/dir1
should work just fine. – Ramesh Oct 03 '14 at 00:29 -
1Ok, just picked my curiosity. I'd like to suggest you improve your answer with an actual example using
pushd
andpopd
to traverse a directory tree back and forth. Your answer is already the correct one. – GnP Oct 03 '14 at 00:35
To answer your question regarding "more history". No the cd -
feature in Bash only supports a single directory that you can "flip" back to. As @Ramesh states in his answer. If you want a longer history of directories you can use pushd
and popd
to save a directory or return to a previous one.
You can also see the list of what's currently in the stack with the dirs
command.
A detailed explanation can be found from this answer titled: How do I use pushd and popd commands?.
You can install and use my dirhistory utility for bash.
Basically, it's a daemon that collects directory changes from all your shells, and a Cdk program that displays the history and lets you pick any directory to switch to (so you're not limited to a stack).

- 27,160
You have as much history as you want:
cd() {
[ "$((${DIRSTACKMAX##*[!0-9]*}0/10))" -gt 0 ] &&
set -- "$@" "$DIRSTACK" &&
DIRSTACK='pwd -P >&3; command cd' ||
{ command cd "$@"; return; }
_q() while case "$1" in (*\'*) : ;; (*)
! DIRSTACK="$DIRSTACK '$2$1'" ;;esac
do set -- "${1#*\'}" "$2${1%%\'*}'\''"
done
while [ "$#" -gt 1 ]
do case ${1:---} in (-|[!-]*|-*[!0-9]*) : ;;
(*) eval " set $((${1#-}+1))"' "${'"$#}\""
eval ' set -- "$2"'" $2"'
set -- "${'"$1"'}" "$1"'
;;esac; _q "$1"; shift
done
eval " DIRSTACK=; $DIRSTACK &&"'
_q "$OLDPWD" &&
DIRSTACK=$DIRSTACK\ $1
set "$?" "${DIRSTACK:=$1}"'" $1
" 3>/dev/null
[ "$(($#-1))" -gt "$DIRSTACKMAX" ] &&
DIRSTACK="${DIRSTACK% \'/*}"
unset -f _q; return "$1"
}
That's a shell function that should enable any POSIX compatible shell to offer zsh
-style cd
history. It does all of its work without invoking a single subshell, and I believe its flow is pretty sound - it seems to handle all cases correctly under moderate testing.
The function attempts to play as nicely with its environment as it may while still relying on fully portable syntax - it makes only one assumption and that is that the $DIRSTACK
environment variable is its property to do with as it will.
It canonicalizes all paths that it stores in $DIRSTACK
and serializes all of them on single-quotes - though it ensures each is safely quoted and serialized before adding it to the value of the variable and shouldn't have any issue with any special characters of any kind. If the $DIRSTACKMAX
environment variable is set it will use it as an upper limit for the number of paths it retains in history, else the limit is one.
If you load the function you just cd
as normal but will also be able to do the cd -[num]
for retracing back through your change directory history.
The function's primary mechanism is cd
itself - and the ${OLD,}PWD
environment variables. POSIX specifies that cd
change these for every path move - and so this just uses the shell's builtin variables and saves the values for as long as you like.

- 58,310
The acd_func.sh script does exactly what you describe. Essentially it overloads the cd
function and enables you to type cd --
to get a list of previously visited directories, from which you can select by number. I find it very hard to use bash without this anymore, and its the first thing I install on a new system.

- 10,519

- 141
Others already covered some interesting solutions. Some time ago I created my own solution to a related problem that could be quickly modified to do "straight history". I basically wanted to "label" a few commonly used directories, and wanted all open shells to see them, and for them to persist between reboots.
#dir_labels
#functions to load and retrieve list of dir aliases
function goto_complete {
unset dir_labels
declare -A dir_labels
{
while read line; do
ll_pre="${line%% *}"
ll_dir="${line#* }"
dir_labels["$ll_pre"]="$ll_dir"
done
} < ~/.dir_labels
unset ll_pre
unset ll_dir
local cur possib
cur="${COMP_WORDS[COMP_CWORD]}"
possib="${!dir_labels[@]}"
COMPREPLY=( $(compgen -W "${possib}" -- ${cur}) )
}
complete -F goto_complete goto
function goto {
unset dir_labels
declare -A dir_labels
{
while read line; do
ll_pre="${line%% *}"
ll_dir="${line#* }"
dir_labels["$ll_pre"]="$ll_dir"
done
} < ~/.dir_labels
unset ll_pre
unset ll_dir
if [ $# -gt 0 ]; then
key="$1"
else
key=default
fi
target="${dir_labels[$key]}"
if [ -d "$target" ]; then
cd "$target"
echo "goto $key: '$target'"
else
echo "directory '$target' does not exist"
fi
}
function label {
unset dir_labels
declare -A dir_labels
{
while read line; do
ll_pre="${line%% *}"
ll_dir="${line#* }"
dir_labels["$ll_pre"]="$ll_dir"
done
} < ~/.dir_labels
unset ll_pre
unset ll_dir
if [ $# -gt 0 ]; then
target="$1"
else
target="default"
fi
dir_labels["$target"]=$PWD
for i in "${!dir_labels[@]}"; do
echo "$i ${dir_labels[$i]}"
done > ~/.dir_labels
}
Basically I'd just do label foo
to call the current directory foo
, and then from whatever shell, goto foo
whould cd
directly there. Empty argument: label
would create a default target for goto
.
I didn't bother implementing automated removal of aliases, but otherwise, I'm still using this in a slightly modified form.

- 12,502
You can use my "cd history" function from http://fex.belwue.de/fstools/bash.html
It remembers every directory where you have been and with "cdh" you will see a list of the last 9 directories. Just enter the number and you are back in this directory.
Example:
framstag@wupp:/: cdh 1: /usr/local/bin 2: /var 3: / 4: /tmp/135_pana/1280 5: /tmp/135_pana 6: /tmp/weihnachtsfeier 7: /tmp 8: /local/home/framstag select: 4 framstag@wupp:/tmp/135_pana/1280:
cdh works with autocd aka "cd without cd": you do not have to type cd or pushd.

- 169
I'd like to recommend my extended 'cd' function to you:
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/).

- 21
for bash, basically: instead of using cd use pushd
to change directorys, so they are saved (meaning stacked)
pushd /home; pushd /var; pushd log
To see the stack use dirs
and for easier navigation (to get the numbers of the "stack-entries" use:
dirs -v
Output:
me@myhost:/home$ dirs -v
0 /home
1 /var
2 /tmp
Now utilize these numbers with cd
and ~
like:
cd ~1
But now these numbers are rearranged now and position "0" will change, so just pushd
the directory to the top position twice (or use a dummy on position 0) like:
me@myhost:/home$ dirs -v
0 /home
1 /home
2 /var
3 /tmp
now 1..3 will keep there position I read this somewhere but do not know anymore, so sorry for not giving credit
(to release the current directory from the stack/deleting it from history use popd
)

- 2,924
See the cdh function in "Shell Programming, 4e" on page 312. It keeps the history in an array.
Here's a more advanced version: https://drive.google.com/open?id=0B4f-lR6inxQWQ1pPZVpUQ3FSZ2M
It Stores the history in the file CDHISTFILE and allows changing to the most recent directory that contains a string, e.g.,
cd -src
It installs itself over the existing cd command by doing an alias cd=_cd

- 11
I tried the answer @mikeserv gave, but it didn't quite work for me. I couldn't figure out how to fix it, so I just wrote my own:
cd() {
# Set the current directory to the 0th history item
cd_history[0]=$PWD
if [[ $1 == -h ]]; then
for i in ${!cd_history[@]}; do
echo $i: "${cd_history[$i]}"
done
return
elif [[ $1 =~ ^-[0-9]+ ]]; then
builtin cd "${cd_history[${1//-}]}" || # Remove the argument's dash
return
else
builtin cd "$@" || return # Bail if cd fails
fi
# cd_history = ["", $OLDPWD, cd_history[1:]]
cd_history=("" "$OLDPWD" "${cd_history[@]:1:${#cd_history[@]}}")
}
This is also available as a GitHub Gist. To use this, just paste the function into your .bashrc
or similar, and you'll be able to do things like cd -5
to go back to the 5th last directory you've been in. cd -h
will give you an overview of your history.

- 111
Just wanted to add fzf-marks as a possible solution.
Once installed it gives you the commands mark and jump to add and search for bookmarked directories (yes, the it's not exactly the complete history, only the ones you bookmarked yourself).
The problem I have with pushd/popd it session specific behaviour, i.e. I'd like to have the same stack on different bash session or so which is possible for fzf-marks.

- 326
I wrote my own cdhist tool for this about 16 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.

- 161
here another implementation, history and also locate usage: https://github.com/joknarf/cdhist (compatible ksh / bash / zsh / ash shells linux* / macos / ish / msys2 / implementation...)
After sourcing cdhist in your shell you will get extended cd command to add history of all visited directories (saved in file, which means will remains after shell session closed) and search/choose directory from history.
cd command alias
- cdhist aliases cd command to function that will push cd history to file.
by default history saved to ~/.cd_history
- CDHISTFILE environment variable can be used to override default
- changing current working dir using cd will add directory to $CDHISTFILE
- calling
cd --
without option will display last $CDNBDIRS (default 10) visited directories, number or<pattern>
can be used to change dir/refilter list - calling
cd -- [<pattern> ...]
will search for regexp<pattern>.*[<pattern>]...
in $CDHISTFILE.- if found and unique will directory change to this directory
- if multiple matches, will display directories list to choose the one to cd
- can choose directory by number
- or give another
<pattern>
to refilter result
- calling
cd - <pattern> ...
will change to last directory in $CDHISTFILE that is matching patterns cd-
is alias forcd -
cd--
is alias forcd --
cdlocate
cdlocate
function (aliascdl
) useslocate
command (mlocate or plocate) to search for directoriescdl <pattern> [<pattern>...]
to search for directories match in locate db (dot directories like .git excluded)- if match found and unique change to directory
- if not unique displays list of found matching directories
- choose directory to change using number
- or give pattern to filter result for another list
code: (compatibility with bash/ksh/zsh/ash)
unalias cd 2>/dev/null # avoid re-entrance
type typeset >/dev/null 2>/dev/null || alias typeset=local # ash no typeset
touch ${CDHISTFILE:=~/.cd_history}
change directory and updpate CDHISTFILE
function cdpush {
typeset cdhf=${CDHISTFILE:-~/.cd_history}
case "$1" in
--) shift; cdhist "$@"; return $?;;
-) [ "$2" ] && { cdhist "$@"; return $?; };;
~*) set -- "$HOME${1#~}";;
esac
\cd "$@" || return $?
echo "$PWD" >"$cdhf.tmp"
grep -v "^$PWD$" "$cdhf" >>"$cdhf.tmp"
[ -s "$cdhf.tmp" ] && \mv "$cdhf.tmp" "$cdhf"
return 0
}
function cdselect
{
typeset dirs="$1" resp
while true
do
[ "$dirs" ] || return 1
[[ "$dirs" != $'\n' ]] && { cdpush "$dirs" ; return $?; }
echo "$dirs"| GREP_COLORS='ms=01;34' GREP_COLOR='1;34;49' grep --color=auto -n "." 2>/dev/null
printf "cd: "
read resp
case "$resp" in
'') break;;
[!0-9]) dirs=$(echo "$dirs" |egrep "$resp" 2>/dev/null);;
) cdpush "$(echo "$dirs" |awk NR==$resp)"; return $?;;
esac
done
}
cdhist : Display CDNBDIRS history
cdhist -a : whole history
cdhist [-a] <pattern> [<pat>...]: display dirs matching pattern/cd to dir if unique found
function cdhist {
typeset gr=. nb=${CDNBDIRS:-10} cdhf=${CDHISTFILE:-~/.cd_history} first='' awk=awk dir
[ ! -s "$cdhf" ] && echo "$CDINITDIRS" >$cdhf
[ "$1" = -a ] && nb=-1 && shift
[ "$1" = - ] && first="exit" && shift
[ -x /bin/nawk ] && awk=nawk
[ "$1" ] && gr=$($awk 'BEGIN{OFS=".*";for(i=1;i<ARGC;i++) $i=ARGV[i];print}' "$@")
cdselect "$($awk 'n == nb {exit} $0 == pwd {next} $0 ~ gr {n++;sub("^"hom,"~");print;'$first'}' gr="$gr" pwd="$PWD" nb=$nb hom=$HOME $cdhf)"
}
use locate to get directories
function locatedir {
typeset file gr awk=awk
[ "$1" ] || return
[ -x /bin/nawk ] && awk=nawk
gr=$($awk 'BEGIN{OFS=".";for(i=1;i<ARGC;i++) $i=ARGV[i];print}' "$@")
shift $(($# - 1))
locate -r "$gr" |awk -F'/' '//./{next}$NF ~ gr' gr=${1##/} |while read file
do
[ -d "$file" ] && echo "$file"
done
}
function cdlocate {
cdselect "$(locatedir "$@")"
}
alias cd=cdpush
alias cd-='cd -'
alias cd--=cdhist
alias cdl=cdlocate
github link has also cd +
and cd ++
extension to navigate through subdirectories of current dir.

- 1
-
1While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review – Philippos Jun 26 '23 at 12:07
-
I agree with !Philoppos. Is
cd --
the main point that this animation demonstrates? Then say so in your answer and explain in text what it does without relying on the animation.. – Rizzer Jun 27 '23 at 17:24 -
As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center. – Community Jun 27 '23 at 17:25