Extending the bash 'cd' command in Linux
In Linux shells, the cd
command is generally built into
the shell. We can add a new cd
program, and even arrange
for it to be executed, but it probably won't have any effect.
The problem is that, although a new cd
utility can
change its own current working directory, it's not clear
how it can change the working directory of the shell that launched it.
Some Unix variants to provide a way to do this. To be honest, I'm not
sure if it's just difficult in Linux, or actually unsupported.
In any case, if you want to make cd
more smart, you'll
probably have to look for a way to extend the functionality of the
built-in cd
. And cd
certainly
could be smarter. Here are a few things that cd
might do, but doesn't:
maintain a list of previously-visited directories, so you can backtrack easily;
allow incomplete or mis-spelled directory names (although widespread use of filename completion has rendered this less important);
maintain a list of previously-visited directories, so the user can select from a list, or using a partial name.
The problem
There are various ways to extend the cd
command, as a simple web search will reveal. However, many of them use peculiar or old-fashioned methods. With modern versions of bash, and perhaps other shells, extending built-in
commands is easy. It is, however, not very well documented.
The solution
Implement your new cd
command so that it takes its argument
from the command line in the usual way, and outputs the new directory
to standard out. It doesn't matter how this directory is arrived at --
from the command-line arguments, of from some list or algorithm -- but
it should be written to standard out, followed by a line break.
Suppose this utility is called my_cd
, for the sake of
discussion.
Now execute the following shell script in the current shell session. By
"in the current session" I mean that the script needs to be executed
using source
or .
-- if it's run as a separate
script, it will only affect that script's environment, which will be
transient.
cd() { CD=`my cd "$@"` builtin cd "$CD" }
What this script does is to replace the built-in cd
with
the two lines of code in the function. The first invokes my_cd
with the same command line as was passed to cd
. It then
stores the program's output -- which will be a directory name --
in the environment variable CD
. The second line of
code invokes the built-in cd
, passing the contents of the
CD
variable as an argument.
The double-quotes in this example are important for controlling the way the shell works. In particular, if a directory name contains spaces, we want to avoid the name being split into separate arguments.
And that's all there is to it.
Gotchas
Well, almost...
The first thing to be aware of is that the my_cd
utility -- whatever it does -- must write a directory name
to standard out, even if it doesn't plan to change directory. The
built-in cd
will expect some input. If you don't want
to change directory, just write "." as the directory name.
Second, be aware that my_cd
can only write the
directory name to standard out, and nothing else. If the program needs
to interact with the user via the console, this needs careful handling.
To write a simple, one-line error or informational message, it's
probably safe to write it to standard error -- the script is
only capturing standard out. For more sophisticated interaction, however,
it's better to work directly with /dev/tty
, and not
use stdin/stdout/stderr at all.
Example
You can see a complete example of this technique in myqcd
utility
on GitHub.