cd - can move to the last visited directory. Can we visit more history other than the last one?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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:

Method 2
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
Method 3
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?.
Method 4
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).
Method 5
You have as much history as you want:
cd() {
[ "$((${DIRSTACKMAX##*[!0-9]*}0/10))" -gt 0 ] &&
set -- "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c8ec88">[email protected]</a>" "$DIRSTACK" &&
DIRSTACK='pwd -P >&3; command cd' ||
{ command cd "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="220662">[email protected]</a>"; 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.
Method 6
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.
Method 7
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.
Method 8
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:
[email protected]:/: 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 [email protected]:/tmp/135_pana/1280:
cdh works with autocd aka “cd without cd”: you do not have to type cd
or pushd.
Method 9
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/).
Method 10
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:
<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ee838bae839786819d9a">[email protected]</a>:/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:
<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a3cec6e3cedacbccd0d7">[email protected]</a>:/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)
Method 11
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
Method 12
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 "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8bafcb">[email protected]</a>" || 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.
Method 13
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.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0
