Shell builtins · bash builtin
pushd
Change to a directory and remember the previous one on a stack, so popd can return to it.
pushd [-n] [DIR | +N | -N]
bash keeps a directory stack: a list of directories whose top entry is always the current
one. pushd DIR changes to DIR and pushes it on top, keeping the old directory underneath.
popd removes the top entry and changes back to the one below. dirs shows the stack;
dirs -v numbers the entries, which is what +N arguments refer to.
Compared with cd -, which remembers only one previous directory, the stack can hold any
number, and pushd with no argument swaps the top two, toggling between two places. In
scripts it is a readable way to "go there, do something, come back", although a subshell
( cd dir && cmd ) is often simpler and cannot forget to return.
pushd and popd print the whole stack after every change. That is helpful at the prompt
and noise in a script, so scripts usually add >/dev/null.
Examples
Change directory and remember where you were
pushd data
pwd
pushd ../logs
dirs -v
~/demo/data ~/demo /home/dev/demo/data ~/demo/logs ~/demo/data ~/demo 0 ~/demo/logs 1 ~/demo/data 2 ~/demo
Each pushd prints the stack with the new current directory first. ~ abbreviates
/home/dev. dirs -v shows one entry per line with its index; entry 0 is always the
current directory.
Go somewhere and come back with popd
pushd /etc >/dev/null
echo "now in $PWD"
popd >/dev/null
echo "back in $PWD"
now in /etc back in /home/dev/demo
popd removes the top entry and returns to the directory below it. The redirection
silences the stack printout, which is the usual style in scripts.
Toggle between two directories
pushd src >/dev/null
pushd >/dev/null; echo "1: $PWD"
pushd >/dev/null; echo "2: $PWD"
pushd >/dev/null; echo "3: $PWD"
1: /home/dev/demo 2: /home/dev/demo/src 3: /home/dev/demo
With no argument, pushd swaps the two top entries and changes to the new top, so each
call flips between demo and demo/src.
Rotate the stack with +N
pushd /tmp >/dev/null; pushd /etc >/dev/null; pushd /usr >/dev/null
dirs -v
pushd +2 >/dev/null
echo "after pushd +2:"
dirs -v
0 /usr 1 /etc 2 /tmp 3 ~/demo after pushd +2: 0 /tmp 1 ~/demo 2 /usr 3 /etc
pushd +N rotates the stack so that entry N (counted from 0 at the top, as dirs -v
shows) comes to the top, and changes there. The other entries keep their cyclic order.
Add a directory without changing to it
pushd -n /var/log >/dev/null
pwd
dirs -v
/home/dev/demo 0 ~/demo 1 /var/log
-n adds the directory to the stack (second place, just below the current directory)
but stays put. A later pushd +1 or popd can jump there.
Pushing a directory that does not exist
pushd no-such-dir
echo "status $?, still in $PWD"
/bin/bash: line 1: pushd: no-such-dir: No such file or directory status 1, still in /home/dev/demo
Nothing is pushed and the current directory does not change; pushd returns 1. In a
script, use pushd dir || exit so later commands do not run in the wrong place.
Run commands in another directory in a script
#!/usr/bin/env bash
set -e
pushd src >/dev/null
echo "building in $PWD"
ls *.c
popd >/dev/null
echo "back in $PWD"
bash build.sh
building in /home/dev/demo/src main.c back in /home/dev/demo
The commands between pushd and popd run inside src, then the script returns to
where it was. With set -e, an error between them would exit without the popd, which
is fine in a script but a reason to prefer ( cd src && ... ) in a function.
The subshell alternative
( cd src && ls *.c )
echo "still in $PWD"
main.c still in /home/dev/demo
A subshell has its own current directory, so the cd cannot leak out, even if a
command fails. Use this when nothing from inside needs to change the parent's variables.
Common pitfalls
Forgetting to silence
pushd/popdin scripts; they print the stack on every call. Redirect stdout to/dev/null.Unchecked
pushd: if the directory is missing, the script keeps running in the old one. Writepushd dir >/dev/null || exit 1.Mixing
cdandpushd:cdreplaces the top of the stack instead of pushing, so a laterpopdreturns somewhere you did not expect.The stack belongs to the current shell. A
pushdinside$(...), a pipeline or a script you run does not affect your terminal.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
pushd,popdanddirsare not POSIX and dash does not have them;#!/bin/shscripts fail withpushd: not found. bash and zsh both have them, with zsh adding options such asAUTO_PUSHD.
Options worth knowing
A selection, described in our own words. For the full list, see bash(1), SHELL BUILTIN COMMANDS.
| Option | What it does |
|---|---|
-n | Add the directory to the stack without changing to it. |
Related
popdRemove the top directory from the directory stack and change back to the next one.cdChange the shell's current working directory.pwdPrint the full path of the current working directory.- Subshells and command groupingThe difference between ( ... ) and { ...; }, what a subshell inherits and what it cannot change, which constructs create one, and what that costs.
- Running scriptsThe four ways to start a script (./script, bash script, PATH, source), what each one changes, and the errors you get when one goes wrong.