sd -- a drop-in replacement for `cd'

Quick Start Guide

Preliminary Remarks

We presume you are using either ksh or a mostly ksh compatible shell (i.e. bash or zsh). sd can be used with tcsh, too, but there are some minor restrictions and somewhat reduced performance.

After successful installation (see Initial Setup), the file sd.ksh has to be sourced from within a running shell. For routine use, the standard way to achieve this is to include the line

. sd.ksh

verbatim (including the leading ". ") in your resource file (e.g. .kshrc or .bashrc). sd.ksh defines some shell functions whose names all start with the string "sd". Relevant for interactive usage are only two of them, namely

sdirs (aliased to "dirs")

where sd is responsible for executing the desired cd command while (s)dirs acts as interface to the other functions. Presuming there is no shell function cd defined at the time sd.ksh is sourced, such a function is automatically defined as

function cd {
   sd "$*"

so that sd can be used transparently instead of cd. Altogether, usually you only use the commands cd and dirs to interact with sd.


If a valid path to a directory is specified as argument to sd (or to the cd function as defined above) it acts just as the builtin cd command (including the cases where the argument is omitted or equal to -).

If the argument is not a valid path, it is interpreted as a regular expression pattern and matched against a stack of directory names of previously visited directories ordered by frequencies of visit. The first match found (if any) is then used for the cd action. By definition this is most probably correct (simply because that directory was the most frequently visited one of all the directories matching the pattern) but is obviously not always what you want. In this case the most direct strategy is to use a more specific pattern.

Full regular expressions can be used but usually they are not necessary. If they are used, take care to use adequate quoting. The most useful patterns are either just substrings of the desired directory name (e.g. its basename or a trailing part of the full path name). In order to enforce a match at the end of the stack entries use something like

cd basename$

Instead of making the pattern directly match the directory you are interested in, it's sometimes easier to issue

dirs pattern

where pattern is a usually non-unique match for the respective directory. This command will display a list of all "hits" of the specified pattern together with a rank index. This rank in turn can then be used to go to the desired directory:

cd =rank_index

With zsh you have to escape the equality sign: cd \=rank_index.

Issuing dirs without an argument displays the complete content of the currently available directory stack. Detailed usage information can be obtained by issuing

dirs -m


The directory stack

The directory stack used by sd for lookup of directories is changing dynamically and is generated/updated as follows:

  1. At startup the stack is initialized from the trailing $sdlines (default: 512) lines/directory names in the logfile of previously visited directories (default logfile: ~/.sd/dirv) by computing a frequency distribution of all unique names.

  2. The resulting list constitutes the directory stack which is queried top-down in order of decreasing frequency when looking for an entry matching the pattern specified for a cd action.

  3. After each successful cd action, the name of the new working directory is appended to the logfile and the stack is regenerated from the (now slightly changed) $sdlines trailing entries. Naturally, if a certain directory is visited sufficiently often, over time it will move up the directory stack.

Obviously, the stack content and, especially, its order is sensitively controlled by the chosen value of $sdlines. This value indirectly defines the effective time window inspected by sd: by default the last 512 cd's are tracked which, at moderately heavy use, does correspond to about 1-2 weeks of work.

Increasing the value of $sdlines extents the effective time window and thus includes more distinct directory names in the stack. However, the stack ordering does no longer change as rapidly. Rather, for large values of $sdlines it approaches a nearly static sorting order. The bottom line: there is to be made a choice between including a sufficiently large number of directories in the stack and a stack that adjusts its sorting order rapidly to a change of focus of the ongoing work (accompanied by frequent visits to a different group of directories).

A further point to realize is the following: the directory stack is maintained in a shell variable ($sdlist). The stack is thus internal to the respective shell process. Especially, it does not change if another running shell instance modifies the logfile (and it's own incarnation of the stack). In order to "synchronize" the stack across different shells (usually in different terminals) stack regeneration can be enforced via dirs -f (it might be easier to just open another terminal, though).

The logfile

The logfile size is limited to $sdmax entries/lines (default: 8192). If this limit is reached, the file is pruned to the 3/4*$sdmax = 6144 trailing/most recent entries. Thus, the accessible history fluctuates between these two figures. This approach ensures that pruning occurs only vary rarely (every few months, probably) and that the minimum time window is that of the last 3/4*$sdmax cd actions (many months, maybe over a year). dirs -i reports the time of the last pruning.

Handling of non-matching patterns

Whether a

cd pattern

command fails, i.e. whether pattern does not match any entry in the current directory stack, partly depends on the chosen value of $sdlines (which might be modified in the corresponding resource file or increased interactively via "dirs -l num").

As an attempt to improve handling of such initially failing cd actions, sd implements the following strategy (also covering the case of "stale" entries, i.e. entries pointing to a directory that has been deleted in the meantime):

  1. If cd pattern fails, first try to find another matching entry further down on the stack.

  2. If this fails too, temporarily increase $sdlines to the total number of entries in the logfile (usually several 1000).

  3. Recreate the stack (which then contains all directories found in the logfile) and try again to find a matching entry (again, skipping over stale entries).

  4. Reset $sdlines and recreate the stack.

In this way the chance of complete failure is distinctly reduced but it is of course not guaranteed that the top-most "hit" on the extended stack is the desired one. For this reason, a list of all hits on the extended stack is displayed, too, in order to enable the user to refine the search pattern if need be. To avoid this output define this shell variable: sdsilent=1 .