Fun with pushd
Here’s about as esoteric a post as I ever write, my love of pushd
and it’s little abused directory stack. If you don’t live on the
command line, move along, there is nothing to see here.
Both Bash and TCSH have a built in command pushd
and a companion
popd
. Both shells work the same way, pushd somewhere
saves (pushes)
the current working on to a stack and then cd
s to
somewhere. popd
reverses the process, popping the newest directory
off the stack and cd
ing to it.
pushd
predates modern window systems. It’s raison d’être is to
let you get back to some deeply nested path when you need to change
directories. Nowadays, you could just open another window but, even in
the modern error, pushd
still has a trick up it sleeve, it’s stack.
First, being lazy, I alias it to pd
:
BASH:
alias pd=pushd
TCSH
alias pd pushd
My trick is to use the stack to copy or move files to some deeply nested directory. Commonly, I’m at work on a project deep in some directory structure. I download a new asset and I need to copy it in to the project.
I could switch the download directory, taking advantage of the save dialog’s ⌘-G shortcut. But when I do, the next time I save something I end up forgetting to switch back, saving the new download in that directory, and not being able to find it.
Another simple option is to take advantage of OS X’s open
command. Running:
open -R ~/Downloads/the-file
open the-destination-directory
will open two Finder windows, one with the download file highlighted and one with the destination folder open. After that, it’s just drag and drop.
But that’s not me. GUIs, who needs them. Here’s how I do it:
BASH version:
pd ~/Downloads
cp the-file ${DIRSTACK[1]}
DIRSTACK
is a shell variable containing an array which is the
directory stack. The first element is the current directory which is
why we use an index of 1.
In TCSH it’s even simpler, the directory stack is exposed through
special variables prefixed with =
. =1
is the previous directory,
=2
the one beyond that and so on.
pd ~/Downloads
cp the-file =1
We can put these together in to functions/aliases for simplicity of use:
function stack-cp() { cp "$@" ${DIRSTACK[1]} ;}
alias stack-cp 'cp \!* =1'
Giving you a work flow like:
pwd
/deeply/nested/directory
pd ~/Downloads
stack-cp imporant-file.txt
However, you do loose something with this approach. On of the nice features of both BASH and TCSH is that they will Tab-expand variables that are obviously file paths. Say you are in a Rails project and you want to copy in a new image into the assets directory:
pwd
/top/off/a/rails/project
pd ~/Downloads
cp new-image.png ${DIRSTACK[1]}/ap<Hit TAB>
and the command line will magically change to
cp new-image.png /top/off/a/rails/project/app/
Typing out ${DIRSTACK[1]}
is a bit much however, so we modify our
pd
to be a function instead of an alias like so:
function pd() { pushd $@ && l=${DIRSTACK[1]} ;}
We get $l
(for last) which can be used it place of ${DIRSTACK[1]}
.
popd
would then change the stack without updating $l
, so that
needs to become:
function popd() { builtin popd $@ && l=${DIRSTACK[1]} ;}
(builtin command
is the BASH equivalent of calling super
.)
You’ll get the same effect in TCSH with the much easier to type =1
pwd
/top/off/a/rails/project
pd ~/Downloads
cp new-image.png =1/ap<Hit TAB>
However, that expands to
cp new-image.png =1/app
which isn’t as obvious.
Yeah, yeah, you’re probably never going to use this the way I do. But, now you know a bit more about what’s happening in your shell and a trick or two when you need them.
Comments