What is a fast command line way to switch between multiple directories for system administration? I mean, I can use pushd .
and popd
to toggle, but what if I want to store multiples and cycle through them, rather than permanently popping them off the bottom of the stack?

- 691
13 Answers
bash
's builtin pushd
with the +
and -
options can rotate the directory stack. The syntax can be a little confusing, perhaps because that stack is a zero-based array. These simple wrapper functions cycle through the directory stack:
# cd to next directory in stack (left rotate)
ncd(){ pushd +1 > /dev/null ; }
# cd to previous directory in stack (right rotate)
pcd(){ pushd -0 > /dev/null ; }
Test: set up a stack of four directories.
dirs -c # clear directory stack
cd /home ; pushd /etc ; pushd /bin ; pushd /tmp
Now /tmp is the current directory, and the stack looks like:
/tmp /bin /etc /home
Change to next directory in stack, (and show it), four times:
ncd ; pwd ; ncd ; pwd ; ncd ; pwd ; ncd ; pwd
Output:
/bin
/etc
/home
/tmp
Change to previous directory in stack, (and show it), four times:
pcd ; pwd ; pcd ; pwd ; pcd ; pwd ; pcd ; pwd
Output:
/home
/etc
/bin
/tmp
A note on cd -
: Wildcard's answer helped illustrate how cd -
doesn't use the $DIRSTACK array, (it uses the $OLDPW variable), so that cd -
doesn't effect $DIRSTACK the way a stack-based swap should. To correct that, here's a simple $DIRSTACK-based swap function:
scd() { { pushd ${DIRSTACK[1]} ; popd -n +2 ; } > /dev/null ; }
Test:
dirs -c; cd /tmp; \
pushd /bin; \
pushd /etc; \
pushd /lib; \
pushd /home; \
scd; dirs; scd; dirs
Output:
/bin /tmp
/etc /bin /tmp
/lib /etc /bin /tmp
/home /lib /etc /bin /tmp
/lib /home /etc /bin /tmp
/home /lib /etc /bin /tmp

- 7,223
-
My frustration is that I want to put items in the stack and keep them there until I choose to clear the stack. That way, I can push, push, push, then cycle through each of three dirs as necessary. Say for instance I need to flip between three directories as if they are like browser tabs, and go back and forth, until I close a tab. Does this address this in a command line way? – Volomike May 30 '16 at 03:45
-
5Yes, I believe it does, try it out. To remove the top item, do
popd
, to remove it without changing dirs, dopopd -n
. To clear the stack dodirs -c
. Too bad the bash designers buried such useful functions in non-intuitive syntax. Seeman bash
underpushd
,dirs
, etc. – agc May 30 '16 at 03:56 -
Works! And was clear as mud in the docs. I like the simplicity of it, now that I know the technique. – Volomike May 30 '16 at 04:05
Use pushd
and then the special names for the directories in your directory stack: ~1
, ~2
, etc.
Example:
tmp $ dirs -v
0 /tmp
1 /tmp/scripts
2 /tmp/photos
3 /tmp/music
4 /tmp/pictures
tmp $ cd ~3
music $ dirs -v
0 /tmp/music
1 /tmp/scripts
2 /tmp/photos
3 /tmp/music
4 /tmp/pictures
music $ cd ~2
photos $ cd ~4
pictures $ cd ~3
music $ cd ~1
scripts $
The most effective way to use pushd
in this way is to load up your directory list, then add one more directory to be your current directory, and then you can jump between the static numbers without affecting the position of the directories in your stack.
It's also worth noting that cd -
will take you to the last directory you were in. So will cd ~-
.
The advantage of ~-
over just -
is that -
is specific to cd
, whereas ~-
is expanded by your shell the same way that ~1
, ~2
, etc. are. This comes in handy when copying a file between very long directory paths; e.g.:
cd /very/long/path/to/some/directory/
cd /another/long/path/to/where/the/source/file/is/
cp myfile ~-
The above is equivalent to:
cp /another/long/path/to/where/the/source/file/is/myfile /very/long/path/to/some/directory/

- 36,499
-
1
-
cd -
acts like a ForthDROP DUP
, e.g. if one starts with in directory a, with stack a b c d, acd -
changes the stack to b b c d. Perhaps that's abash
bug however, since a secondcd -
changes the stack back to a b c d. From this we can infer thatcd -
uses some separate data structure to store the previous directory, in addition to changing the stack. – agc May 31 '16 at 21:42 -
3
-
So maybe
cd -
was the earlier bash feature, then later they added a stack, but feared breaking the old code more than they wanted a simpler design. Correction to previous,cd -
acts something like a ForthOLDPWD @ SWAP OLDPWD ! DUP .
Unlikecd foo
, thecd -
prints the name of the swapped-in directory. – agc May 31 '16 at 22:46 -
Or there's always the interactive pushd wrapper (other answers to that question include autojump etc. etc.). – Useless Jun 01 '16 at 14:08
-
-
@Tommiie note the “bash” tag on this question. It may also work in ksh and zsh, but tcsh has a wildly different syntax—and I strongly recommend not using it. (There are some good articles on why tcsh is not recommended, but I’m on mobile so don’t have links handy.) – Wildcard Apr 01 '19 at 09:12
I suggest you install fasd. It gives you the ability to quickly jump to any directory you've already been in by typing only a small fraction of its name.
Example: if you've visited /home/someName/scripts/
, you could jump there just by typing z scr
for example. It's way more convenient that remembering the ordering in the history stack or anything similar.

- 231
When you cd
somewhere, Bash stores the old working directory an environment variable, $OLDPWD
.
You can switch back to that directory with cd -
, which is equivalent to cd "$OLDPWD"
.
You can bounce back and forth between directories like so:
blue$ cd ~/green
green$ cd -
blue$ cd -
green$

- 501
I wrote a script named xyzzy
to do this:
#!/bin/bash
i="$1"
i=$((${i//[^0-9]/}))
i="$(($i-1+0))"
b="$2"
b=$((${b//[^0-9]/}))
b="$(($b-1+0))"
if [ -z "$XYZZY_INDEX" ]; then
XYZZY_INDEX="$((-1))"
fi
if [ ! -f "/tmp/xyzzy.list" ]; then
touch /tmp/xyzzy.list
chmod a+rw /tmp/xyzzy.list
fi
readarray -t MYLIST < /tmp/xyzzy.list
showHelp(){
read -r -d '' MYHELP <<'EOB'
xyzzy 1.0
A command for manipulating escape routes from grues. Otherwise known as a useful system admin
tool for storing current directories and cycling through them rapidly. You'll wonder why this
wasn't created many moons ago.
Usage: xyzzy [options]
help/-h/--help Show the help.
this/-t/--this Store the current directory in /tmp/xyzzy.list
begone/-b/--begone Clear the /tmp/xyzzy.list file. However, succeed with a number and
it clears just that item from the stored list.
show/-s/--show Show the list of stored directories from /tmp/xyzzy.list
. # Use a number to 'cd' to that directory item in the stored list. This syntax is odd:
. xyzzy 2
...would change to the second directory in the list
. [no options] Use the command alone and it cd cycles through the next item in the stored
list, repeating to the top when it gets to the bottom. The dot and space before xyzzy
is required in order for the command to run in the current shell and not a subshell:
. xyzzy
Note that you can avoid the odd dot syntax by adding this to your ~/.bashrc file:
alias xyzzy=". xyzzy"
and then you can do "xyzzy" to cycle through directories, or "xyzzy {number}" to go to a
specific one.
May you never encounter another grue.
Copyright (c) 2016, Mike McKee <https://github.com/volomike>
EOB
echo -e "$MYHELP\n"
}
storeThis(){
echo -e "With a stroke of your wand, you magically created the new escape route: $PWD"
echo "$PWD" >> /tmp/xyzzy.list
chmod a+rw /tmp/xyzzy.list
}
begoneList(){
if [[ "$b" == "-1" ]]; then
echo "POOF! Your escape routes are gone. We bless your soul from the ever-present grues!"
>/tmp/xyzzy.list
chmod a+rw /tmp/xyzzy.list
else
echo -n "Waving your wand in the dark, you successfully manage to remove one of your escape routes: "
echo "${MYLIST[${b}]}"
>/tmp/xyzzy.list
chmod a+rw /tmp/xyzzy.list
for x in "${MYLIST[@]}"; do
if [[ ! "$x" == "${MYLIST[${b}]}" ]]; then
echo "$x" >> /tmp/xyzzy.list
fi
done
fi
}
showList(){
echo -e "These are your escape routes:\n"
cat /tmp/xyzzy.list
}
cycleNext(){
MAXLINES=${#MYLIST[@]}
XYZZY_INDEX=$((XYZZY_INDEX+1))
if [[ $XYZZY_INDEX > $(($MAXLINES - 1)) ]]; then
XYZZY_INDEX=0
fi
MYLINE="${MYLIST[${XYZZY_INDEX}]}"
cd "$MYLINE";
}
switchDir(){
MYLINE="${MYLIST[${i}]}"
cd "$MYLINE";
}
if [[ "$@" == "" ]];
then
cycleNext
fi;
while [[ "$@" > 0 ]]; do case $1 in
help) showHelp;;
--help) showHelp;;
-h) showHelp;;
show) showList;;
-s) showList;;
--show) showList;;
list) showList;;
this) storeThis;;
--this) storeThis;;
-t) storeThis;;
begone) begoneList;;
--begone) begoneList;;
*) switchDir;;
esac; shift
done
export XYZZY_INDEX
The way I use this is to copy into /usr/bin
folder and then chmod a+x
on it. Then, I edit my root and user account ~/.bashrc
file to include these lines at the bottom:
alias xyzzy='. xyzzy'
alias xy='. xyzzy'
The 'xy' is a shortened form of the command for faster typing.
Then, I can store the current directory in the list with...
xyzzy this
...and repeat as necessary. Once I fill this list with the directories I need, they remain there until I reboot the computer because that's when /tmp is cleared out again. I can then type...
xyzzy show
...to list the currently saved directories. In order to switch to a directory, I have two choices. One option is to specify the path by index (and it's a 1-based index) like so:
xyzzy 2
...which would switch to the directory that's the second item in the list. Or, I could leave off the index number and just do:
xyzzy
...to have it loop through each directory as I need. For more commands you can do, type:
xyzzy help
Of course, work is more fun with the silly echo statements that I added in.
Note that xyzzy is a reference to the Collosal Cave text adventure, where typing xyzzy would let you switch between two rooms in the game in order to avoid grues.

- 93,103
- 40
- 240
- 233

- 691
-
2+1 I started playing with this. It's nice, but seems to fail if no directories have been added yet and you enter just xyzzy. Needs one more test for MAXLINES -eq 0 in cyclenext(). – Joe Jun 04 '16 at 05:34
There is also cd_func
from Petar Marinov, it's basically cd
with a history of up to 10 entries: http://linuxgazette.net/109/misc/marinov/acd_func.html
# do ". acd_func.sh"
# acd_func 1.0.5, 10-nov-2004
# petar marinov, http:/geocities.com/h2428, this is public domain
cd_func ()
{
local x2 the_new_dir adir index
local -i cnt
if [[ $1 == "--" ]]; then
dirs -v
return 0
fi
the_new_dir=$1
[[ -z $1 ]] && the_new_dir=$HOME
if [[ ${the_new_dir:0:1} == '-' ]]; then
#
# Extract dir N from dirs
index=${the_new_dir:1}
[[ -z $index ]] && index=1
adir=$(dirs +$index)
[[ -z $adir ]] && return 1
the_new_dir=$adir
fi
#
# '~' has to be substituted by ${HOME}
[[ ${the_new_dir:0:1} == '~' ]] && the_new_dir="${HOME}${the_new_dir:1}"
#
# Now change to the new dir and add to the top of the stack
pushd "${the_new_dir}" > /dev/null
[[ $? -ne 0 ]] && return 1
the_new_dir=$(pwd)
#
# Trim down everything beyond 11th entry
popd -n +11 2>/dev/null 1>/dev/null
#
# Remove any other occurence of this dir, skipping the top of the stack
for ((cnt=1; cnt <= 10; cnt++)); do
x2=$(dirs +${cnt} 2>/dev/null)
[[ $? -ne 0 ]] && return 0
[[ ${x2:0:1} == '~' ]] && x2="${HOME}${x2:1}"
if [[ "${x2}" == "${the_new_dir}" ]]; then
popd -n +$cnt 2>/dev/null 1>/dev/null
cnt=cnt-1
fi
done
return 0
}
alias cd=cd_func
if [[ $BASH_VERSION > "2.05a" ]]; then
# ctrl+w shows the menu
bind -x "\"\C-w\":cd_func -- ;"
fi
Siimply use cd --
to show a list of the past up to 10 directories you cd
ed to and cd -N
(where N
is the index of the entry) to go there.

- 5,953
- 7
- 42
- 71
I use a small script called z [link], which might also be of interest, even though it does not do exactly what you asked.
NAME
z - jump around
SYNOPSIS
z [-chlrtx] [regex1 regex2 ... regexn]
AVAILABILITY
bash, zsh
DESCRIPTION
Tracks your most used directories, based on 'frecency'.
After a short learning phase, z will take you to the most 'frecent'
directory that matches ALL of the regexes given on the command line, in
order.
For example, z foo bar would match /foo/bar but not /bar/foo.

- 153
You can achieve this with a bunch of aliases in your ~/.bashrc
(or equivalent)
The main goal is near-minimum typing (e.g. d5
jumps to directory number 5 in the pool) to switch between directories in the pool. Also we want to make it easy to add/remove directories to/from the pool:
alias pd=pushd
alias po=popd
alias d='dirs -v'
alias d0=d
alias d1='pd +1'
alias d2='pd +2'
alias d3='pd +3'
alias d4='pd +4'
alias d5='pd +5'
alias d6='pd +6'
alias d7='pd +7'
alias d8='pd +8'
alias d9='pd +9'
alias d10='pd +10'
# -- feel free to add more aliases if your typical dir pool is larger than 10
Now every time you push a directory on the stack it gets added to the numbered pool at position 0 (the current dir) and you can jump around (switch directories) using very little typing (d<N>
) and you can view your numbered current pool at any time by just typing d
.
A few examples of using these aliases:
Display the numbered dir-pool, (current dir is # 0)
$ d
0 /tmp
1 /
2 /usr
Switch between dirs: use d<N>
$ d2
$ pwd
/usr
Add a new directory to the pool
$ pd /var/log
$ d
0 /var/log
1 /usr
2 /tmp
3 /
Jump around some more:
$ d3
$ pwd
/
$ d3
$ pwd
/tmp
$ d
0 /tmp
1 /
2 /var/log
3 /usr
Remove/pop the top (current) dir from the pool
$ po
$ d
0 /
1 /var/log
2 /usr

- 890
I mostly use ZSH with oh-my-zsh profile. You can type into a terminal the following match:
# cd /ho
Then you can simply use arrows (up and down) to go through all the shell history which shows only those entries that start with the characters above. So, for instance, if you went to /home/morfik/Desktop/
and /home/morfik/something/
, you can switch between the directories very fast. It doesn't matter how many entries in the shell history you have, but when you have a lot, just use a better expression, i.e. cd /home/morf
and here press up/down arrows on you keyboard.
There's also another way to achieve the solution. In this case you have to use tmux and FZF. Then you just make a hotkey, for instance ctrl-r, and when you press it, your current window will be divided and you'll see this:
Now you can use expressions to search the list. I just typed ^cd /media
, which returns only the entries that start with the phrase. Of course, you can just type 'cd 'home
to match the command and also the entire directory name (not path, just name):

- 10,549
If you have iselect installed, you could do something like this:
$ alias dirselect='cd $(iselect -a $(dirs -l -p | sort -u))'
$ dirselect
This will give you a full-screen ncurses-based interactive arrow-key navigable menu to select a directory to cd
to.
If you haven't used pushd
in the current shell session, the list of directories in the menu starts off with just one entry, your current directory. If there's only one entry, this dirselect
alias will just cd
to it without the menu screen, so it effectively does nothing (except prevent cd -
from doing anything useful)
To add a new directory to the list, use pushd dir
(or pushd -n dir
to add a dir without cd
-ing to it at the same time)
You can pre-populate the pushd
stack by doing something like the following in your .bashrc
or ~/.bash_profile
:
for d in /var/tmp /tmp /path/to/somewhere/interesting ; do
pushd -n "$d" > /dev/null
done
You can remove entries with popd
or popd -n
.
See help pushd
, help popd
, and help dirs
in bash for more info. And, of course, man iselect
.
BTW, iselect
is probably available pre-packaged for your distro. It is for Debian and Ubuntu etc, and probably for others too.

- 78,579
If you have 5 or 10 directories you use a lot, and don't necessarily care about recently used 5 or 10 directories, set up some command aliases like
alias cdw="cd /var/www/html"
And so when I want to go to Apache home page directory I just type cdw
as I answered in Equivalent of alias for a symbolic link?.

- 826
I am using jump to quickly change the working directory.
To add the current directory:
jump -a [bookmark-name]
To list all your bookmarks:
jump -l
e.g.:
------------------------------------------------------------------
Bookmark Path
------------------------------------------------------------------
reports ~/mydir/documents/reports
projects ~/documents/projects
dl ~/Downloads
------------------------------------------------------------------
Now you can easily jump to another directory:
jump reports
It supports autocompletion for bash and zsh.
Edit (in response to @Joe): the binary jump-bin
is stored in /usr/local/bin
, and with the bash integration script (in my PC located at /var/lib/gems/1.9.1/gems/jump-0.4.1/bash_integration/shell_driver
) it creates a bash function jump
which calls jump-bin
.

- 533
- 5
- 12
-
Where does the jump command live? I don't have it and don't immediately see where to get it. – Joe Jun 04 '16 at 05:08
I use alias
to navigate. If you have few directories that are frequently accessed, then simply set aliases.
For example,
alias e='cd /etc'
alias h='cd /home'
alias al='cd /var/log/apache2/'
Then simply
e
will take you to /etc
.

- 902
- 7
- 14
$CDPATH
perhaps? – Chris Davies May 30 '16 at 19:13cd -
after googling instigated from this question. – Nacht May 31 '16 at 06:24cd
everywhere by hand. Was painful. Used auto completion with tab key, though. – Volomike May 31 '16 at 18:46