1. Introduction
Working in the shell within a terminal normally necessitates frequent directory switching. Further, moving back and forth between directories can be a way to avoid typing out whole paths or simply the means to perform a task that requires data from two or more locations. For this, we often use the pushd and popd commands.
In this tutorial, we talk about ways to expand or silence pushd and popd, making them behave more like their simple relative cd.
Importantly, the examples we show use the Bash syntax but can also be changed to work with other shells.
We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.
2. Directory Stack
Just like functions in a programming language, directories we visit can be kept within a stack structure. Each element of the stack preserves the last working directory.
As usual, there are two main operations with a stack:
In the case of directory stacks, push usually means switching to the directory while placing it on the stack. On the other hand, pop typically removes the last element and switches back to the previous directory.
This dynamic enables quick changes without needing to remember, save, or type out paths. Still, both have the -n switch to suppress the directory change.
In any case, to visually aid the user in the process, pushd and popd show the current stack as their output:
$ pushd /dir
/dir /
$ pushd ./subdir
/dir/subdir /dir /
$ popd
/dir /
In fact, the dirs Bash builtin can show this information at any point. Notably, this can be helpful when building the stack, since we can consider the path we need to go back through. Further, we know where we currently are relative to other stacked directories. Lastly, the space-separated list can be parsed fairly easily since spaces need escaping in paths.
However, a potential disadvantage of the automatic output from both pushd and popd is the amount of verbose textual information we generate. On top of this, most of the text is repetitive.
3. Universal pushd and popd Output Suppression
Naturally, to prevent a command from showing any output, we can use the standard redirection operators in combination with /dev/null:
$ pushd /dir > /dev/null
$ popd > /dev/null
$
Thus, we drop the output completely.
Furthermore, we can even use a specific file:
$ pushd /dir > /tmp/dirs
$ cat /tmp/dirs
/dir /
$ popd > /tmp/dirs
$ cat /tmp/dirs
/
This way, we preserve the stack after the last operation in the /tmp/dirs file.
In case the potentially huge amount of verbose information from pushd and popd isn’t an issue to store, we can use the >> append operator and save all of it in a file instead:
$ pushd /dir >> /tmp/dirs
$ popd >> /tmp/dirs
$ cat /tmp/dirs
/dir /
/
Thus, we can keep a history of the directory stack. Adding a date or timestamp and username along with the relative command to this log might be beneficial for keeping track of certain activities.
Further, we can apply these augmentations to the current user or the whole system.
4. Global pushd and popd Modification
Naturally, we can always create an alias for both pushd and popd:
$ alias pushd='>/dev/null pushd'
$ alias popd='>/dev/null popd'
$ pushd /dir
$ popd
$
This way, we prevent any output from either command.
Alternatively, we can use functions:
$ function pushd() {
command pushd "$@" >/dev/null
}
$ function popd() {
command popd "$@" >/dev/null
}
We can add further functionality even more conveniently:
$ function pushd() { printf '%s %s %s ' "$(date)" "$(whoami)" "${FUNCNAME[0]}" >>$HOME/.dirs; command ${FUNCNAME[0]} "$@">>$HOME/.dirs; }
$ function popd() { printf '%s %s %s ' "$(date)" "$(whoami)" "${FUNCNAME[0]}" >>$HOME/.dirs; command ${FUNCNAME[0]} "$@">>$HOME/.dirs; }
In this case, we show the date and current user via whoami. After that, we use command and the function name from ${FUNCNAME[0]} in the $FUNCNAME array stack to call the relevant command. All of the output goes to the $HOME/.dirs personal user file as a history of these operations:
$ pushd /dir
$ pushd subdir/
$ popd
$ cat $HOME/.dirs
Fri Jan 11 01:00:04 AM UCT 2024 baeldung pushd /dir / ~
Fri Jan 11 01:00:09 AM UCT 2024 baeldung pushd /dir/subdir /dir / ~
Fri Jan 11 01:00:11 AM UCT 2024 baeldung popd /dir / ~
To apply any of the changes more globally, we can use different files depending on the shell:
- $HOME/.bashrc, $HOME/.zshrc, $HOME/.[t]cshrc: user-specific
- /etc/skel/.profile, /etc/skel/.bashrc, /etc/skel/.zshrc, /etc/skel/.[t]cshrc: system global for new users
- /etc/bash.bashrc, /etc/zsh/zshrc, /etc/[t]csh.[t]cshrc: system global for all users
In particular, we store the relevant configuration in one of the files above according to our preferences.
5. Summary
In this article, we talked about the pushd and popd commands, how to suppress their output, and introduced extensions to their functionality.