Monday, December 28, 2009

Using pushd, popd and dirs

Since I have a bit of time on my hands, I thought I might share a trick or two.  Well, not really 'tricks,' but good information to know. I will cover the shell builtins, pushd, popd, and dirs for bash and zsh. Additionally, I will demonstrate how to use some shell aliases, in order to make the use of these handy builtins even more convenient, and time saving.

Wouldn't it be nice if you could have your shell keep track of the directories where you were, so that you could easily return to them later? If you are using bash or zsh, you already have this capability available to you.

 Say you are working in the directory ~/programming/network/modules.  You wish to cd to another directory, and then return.  There are several ways you could do this, but some are definitely more efficient than others. Consider:

modules $ cd ~/bin
bin $


Now, to get back to the directory ~/programming/network/modules, I hope you would never consider doing this:

bin $ cd ~/programming/network/modules

Of course you could do it that way, but it is such a waste of keystrokes, and of your time.  In this case, it is best to do this:

bin $ cd -

The cd - is a short cut to return to the previous directory.  It only is useful for one directory though.  If you were to issue this command repeatedly, you would flip back and forth between the same two directories.

If you want the shell to remember more directories, you need to use the pushd and popd commands.  By using these two, you can utilise the directory stack to keep track of directories you want to.  Instead of using cd to change directory, use pushd to both push the current directory name on to the stack, and to change to the new directory you wanted.  To return to my previous example, where I was in ~/programming/network/modules, and wanted to change to ~/bin, I would do:

modules $ pushd ~/bin
~/bin ~/programming/network/modules
bin $


Now I am in ~/bin, and the old directory (~/programming/network/modules) is on the stack, saved for future use.  If I wanted to return to the previous directory, I would simply do:

bin $ popd
~/programming/network/modules
modules $


Right back where I started!  Of course, if you were only changing directory once, you would be better off using cd -, as shown previously.  But if you have a great deal of directories you wish to save, push them on to the stack with pushd, and use them later.

How then, do I keep track of where I am at, and what stack frames are available, and where?  Consider the following (a contrived example, but good enough as an illustration):

modules $ cd
~ $ pushd programming
~/programming ~
programming $ pushd network/
~/programming/network ~/programming ~
network $ pushd modules/
~/programming/network/modules ~/programming/network ~/programming ~


As you can see, I issued a cd command which, without options or arguments, changed my current working directory to my home directory, ~/.  Then I pushed 3 directories on to the stack:  ~/programming, ~/network, and ~/modules.  If I use the dirs -v command, I can get a listing of the directory stack:

modules $ dirs -v
 0  ~/programming/network/modules
 1  ~/programming/network
 2  ~/programming
 3  ~
modules $


I pushed ~/ on to the stack first, and you can see now that it is on the 'bottom', underneath all the other frames.

You can also use the dirs command with no options, where you will get a listing of the stack frames, but listed on the same line, with the left side corresponding to the most recent push, and the right side being the first push:

modules $ dirs
~/programming/network/modules ~/programming/network ~/programming ~


You can use the info from dirs -v to make directory changes.  If I wanted to go to ~/programming rather quickly, I would do:

modules $ pushd +2
~/programming ~ ~/programming/network/modules ~/programming/network
programming $


The +2 corresponds to frame 2 of the dirs -v listing above.

Both pushd and popd can take an integer like the above +2, with either '+' or '-'.  If a '+' is specified, +n, then the nth stack frame from the 'top' of the stack (dirs -v, or the 'left' of a dirs listing)' is affected.  If a '-' is specified, -n, then the nth frame from the 'bottom of the stack (dirs -v, or the 'right' of a dirs listing) is affected.  To confuse things a little here, in the zsh, if setopt PUSHD_MINUS is set, then the function of the '+' and '-' are reversed when used with both pushd and popd.

zsh also has a feature where you can use cd with a +n where n corresponds to a stack frame, and the cd will be to that directory.

[kermit@minty]~/programming/network% pwd
/home/kermit/programming/network
[kermit@minty]~/programming/network% dirs -v
0       ~/programming/network
1       /bin
2       ~
3       ~/programming
[kermit@minty]~/programming/network% cd +1
/bin
[kermit@minty]/bin% pwd
/bin


This should be enough to give you an idea of what can be accomplished with pushd and popd.  If you find yourself using these two commands often, you will begin to long for a shortcut.  Place the following in your .bashrc (or .zshrc)

alias -- +='pushd'
alias -- -='popd'


Now you can use + for pushd, and - for popd.

A couple of other thoughts.  First of all, if you want to clear your directory stack, do

dirs -c

Regarding dirs, a useful feature of zsh (which bash doesn't have) is the ability to populate the stack 'manually' with a list, using the dirs command:

[kermit@minty]~% pwd
/home/kermit
[kermit@minty]~% dirs -v
0       ~
[kermit@minty]~% dirs ~/programming ~/programming/network /bin /usr/src
[kermit@minty]~% dirs -v
0       ~
1       ~/programming
2       ~/programming/network
3       /bin
4       /usr/src
[kermit@minty]~%


As you can see, the directory stack was built from the list I entered plus the current working directory.

You might have noticed that every time you do a pushd (or popd), the next line is always the output of a dirs command.  I don't know of any way to silence this with bash, but with zsh, just add the following to your .zshrc:

setopt PUSHD_SILENT

To get the same effect in bash, you could do an alias for pushd and popd:

alias pushd='pushd > /dev/null'
alias popd='popd > /dev/null'

#If you did the +/- alias for pushd/popd as shown above, try

alias -- +='pushd > /dev/null'
alias -- -='popd > /dev/null'


Finally, if you use zsh, and you want to automatically store directory changes on the stack just by moving around with cd, put the following in your .zshrc:

setopt  AUTO_PUSHD

With this option set, every cd command will act like a pushd command, storing your current directory on the stack, and then changing to the new directory.  This will save you a lot of typing!

Conclusion

Unless you like the idea of typing more than necessary, you will want to use pushd, popd, and dirs to your advantage while on the command line, in order to save time, and wear and tear on your wrists/fingers.  As always, consult your local man pages for particulars about the shell that you are using.

No comments:

Post a Comment