Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

some directory changing tools to share



All,

I like zsh very much.  I have learned a lot by being in this mailing list.
I hope I can give something back by sharing some tools that I wrote and
which I have tried for quite a while.  I find them really useful in
increasing my efficiency and productivity.

My purpose is twofold. First, I hope that someone else may benefit from the
tools, as I have.  Second, someone may be able to improve them.  I try to
add improvements as the needs arise, but there are still imperfections that
I simply tolerate for lack of time and patience.  Also my number 1
objective to get the tools to work, so there are definitely rooms for
better design or more flexibility.  

Any suggestions for enhancement or bug reports will definitely be welcome.
Of course, if you simply like the tools, drop me a line of encouragement.

The documentation below is somewhat long because I wrote it in the form a
tutorial and sometimes I try to explain why some features are needed.  But
believe me, the tools are useful and it is worth reading on.


		       Efficient Tools to Change Dir
		       =============================


This is a set of tools to help one navigate through a huge directory
system.  The objective is to save typing (keystrokes).  I have used them
                             ========================
in my work environment (i.e., in the real world, not just theoretically). 

I sometimes like to describe to others that this is a LAZY man's "cd".  In
reality, it is not because I am really lazy or that I can't type fast
enough.  It is a matter efficiency.  Whatever small amount of time I can
save here, I can use that towards more productive work.  I don't see any
real benefit in being able to remember a long directory name and to type
the entirety of it extremely fast, if one can achieve exactly the same
result by typing only a third as much (and equally extremely fast).

So much for commercial.  Let's get to the meat.  

Installation
============

If you want to try out the tools:

   1. Save the following code snippet in a file called "ktools" (or any
      other name you like).

   2. Make sure you don't have your own aliases that conflict with the
      commands that I use: c, d, sd, unsd, drs.  If you do, read the
      section "Command Name Conflicts" below.

   3. cd to the directory containing the file "ktools"; source the file
      using the command ". ktools".

   4. Try out the commands as described below.

I know most people will frown on my codes because I don't write them in a
neatly documented and traditional way.  I did not originally mean to
write it for others to read - all it matters to me is that they work.  If
enough people like the tools, I'll be glad to shape up the codes.

In reality, the tools are not specific to "zsh".  With some (slight, mostly
syntactic) modifications, I have made them work in "ksh".

#########  cut here; put in file "ktools" or .zshrc  ###############

function c {
	[ "$1" = "" ] && return
	! [ $1 = . ] && ! [ $1 = .. ] && ! [ $1 = ... ] && [ -d $1 ] && { dirname=$1 
		shift } || dirname=`pwd` 
	[ "$1" = + ] && { flag=1 
		shift } || flag=0 
	for _j
	do
		_j=${_j%/} 
		_tmp=0 
		case $_j in
			.) dirname=$dirname/.. 
				continue ;;
			..) dirname=$dirname/../.. 
				continue ;;
			...) dirname=$dirname/../../.. 
				continue ;;
			*) [ -d $dirname/$_j ] && arg=$_j  || arg=`/bin/ls -a $dirname| grep -i "^$_j"` 
				[ "$arg" ] || break ;;
		esac
		for _i in $arg
		do
			[ -d $dirname/$_i ] && { dirname=$dirname/$_i 
				_tmp=1 
				break }
		done
	done
	if ( [ $dirname != $PWD ] && [ $flag = 0 ] ) || [ "$_tmp" = 1 ]
	then
		cd $dirname
	elif [ "$2" != . ]
	then
		c + . $*
	fi
}
function sd { loadn; var=_dir$1; eval $var=${2:-$PWD}; saven }
function unsd { for _i; do sed "/^_dir$_i/d" $HOME/.dirnames > $HOME/.v;
  /bin/mv $HOME/.v $HOME/.dirnames; done }
function d { if [ $1 ]; then if [ $1 = . ]; then cd $_dir; 
  elif [ $1 = - ]; then dm $2;
  else dirname=`grep "^_dir$1" $HOME/.dirnames | sed '1q' | sed 's/.*=//'`
  [ $dirname ] || return; shift; c $dirname $*; fi; else dm; fi; }
function dm { drs $1; echo -n "enter short name: "; read a b; case $a in
  ""|.) cd $_dir;; =) ;;
  -*) b="`echo $a | sed 's/-//'` $b"; eval unsd $b;; *) d $a $b;; esac }
function drs { echo; if [ $1 ]; then
  grep -i $1 $HOME/.dirnames | sed -e 's/^_dir//' -e 's/=/	/'; else
  cat $HOME/.dirnames $_1 | sed -e 's/^_dir//' -e 's/=/	/'; fi; echo ;}
alias d-='d -'
alias saven='set | grep "^_dir" > $HOME/.dirnames'
alias loadn='. $HOME/.dirnames'

alias c.='c .'
alias c..='c ..'
alias c...='c ...'
alias ch='c ~'
alias cm='c ~/Mail'
alias P='c ~/Perl'

#######################  end "ktools"  #############################


Command Name Conflicts
======================

The following is a list of command names used in the tool:

   P c c. c.. c... ch cm d d- dm drs loadn saven sd unsd

If you have been using any of these names for your own commands or aliases,
you have 2 options: (1) change your command and alias names, or (2) change
those in the above tools.  You know my preference, but it is your decision.

Sometimes, you may not have knowingly defined commands or aliases that
conflict with the ktools names - these may have been defined systemwide by
default in your environment.  In any way, if a command in ktools does not
behave as described here, the first thing to look for is whether there is a
hidden command name conflict.  In "zsh", the command "which" will reveal
how a command is defined, e.g. "which c" which shown "c" as a function
definition or alias; compare that with the one in the file ktools. If the
output of "which" does not match the definition in ktools, you have a
conflict and you must change one of the names.

If you add the following lines to the beginning of ktools, they will detect
any name conflicts and temporarily override the previously existing commands
to let you test run ktools.

####################################################################

for i in P c c. c.. c... ch cm d d- dm drs loadn saven sd unsd
do
   case `which $i` in
   *\ not\ found) ;;
   *:\ aliased\ to\ *) echo; echo "**** previous alias for $i disabled"; 
                       which $i; unalias $i;;
   *)                  echo; echo "**** previous command $i no longer accessible"
                       which $i
   esac
done

####################################################################

But you will not be able to access the previous commands (which you may not
need at all). That is why I suggest testing out the tools for a period of
time until you are satisfied that they are useful and there are no
conflicts. Then you can include the ktools codes in the ".zshrc" to be
invoked automatically at login.


Simple Dir Changing
===================

Although the code snippet is not that long, there are more than just a
couple of features and I'll describe them one by one.  

Suppose you are currently in a directory and it has the following
subdirectories:

   Doc
   Faq
   Info
   Letters
   Logs
   Mail
   Perl
   bin
   mail
   paper
   public       (regular non-directory files are not listed)

You notice that I like to capitalize my directory names whenever I can (a
trick that I learned I forgot where - so that the "ls" output puts all
these directories at the beginning).  Unfortunately, there are always a few
exceptions, perhaps because I want to be compatible with my colleagues, or
because some directories are created by some software that is out of my
control.  

Now suppose I want to change dir to "Letters".  The conventional way is "cd
Letters".  The more savvy zsh user may have used "cd Le*" or "cd L<Tab>"
(completion), but neither one of these works well if there are other
non-directory filenames that also begins with "Le" (you can get around one 
of the problems by redefining the completion rules for "cd").

Using my tool, you just type

   c l

Rule 1: "c" picks the first (lexicographical order) directory name that
^^^^^^  starts with "l" (case insensitive).  See later rules for
        exceptions.

So you never need to press <Shift> (one keystroke saved) and you never need
to remember the entire name (is it Doc or Docs, Letter or Letters?)

Other examples: "c lo" goes to "Logs", "c p" to "Perl", "c pa" to "paper"
and "c pu" to "public".


Plan Directory Names Wisely
===========================

You don't need to be told that "c" works best if every subdirectory starts
with a different character (case insensitive).  

If that is not possible, at least avoid having multiple names that have the
same first 2 characters.  Especially avoid having "Mail" and "mail" in the
same directory as in the above example.  Think: how do you go to "mail"
using "c".

Sometimes twisting the directory names a little can help.  For instance, if
you have 3 subdirectories named "Test1", "Test2", "Test3", change them to
"1Test", "2Test", and "3Test".

Recursive Use
=============

Rule 1 works recursively down the directory tree one level at a time, with
unlimited depth.  For instance, if "Perl" has a subdirectory "Test" which
in turn has a subdirectory "Gui" (assuming that "t" and "g" identities
"Test" and "Gui" using Rule 1), then

   c p t g

takes you all the way to Perl/Test/Gui.  If that is a directory that you
frequent a lot, soon you will discover the correct sequence (later we will
learn other even shorter ways).

Rule of Exact Match - and Jumping to a Directory Far Away
=========================================================

One exception to Rule 1 is that 

Rule 2: if there is an exact match (both case sensitiveness AND entire 
^^^^^^  name), then "c" will pick that over Rule 1.  
	
Rule 2 applies to any of the arguments at any level. 

For instance,

   c mail

will change to "mail" instead of "Mail".  However, "c mai" still gives you
"Mail" because "mai" is not an exact match for "mail".  

The major use of Rule 2, however, is in jumping to a directory that can be
far away in the whole directory tree.  For instance, you can be any where,
and

   c ~ p t

will bring to you your home directory (~ is an exact match) and then
traverse down the tree using Rule 1 to match "p" and "t".  (If the example
cited above is actually under your home directory, then this will bring you
to "Perl/Test").  You can use that to go to directories that are not even
under you home directory, e.g,

   c / us op l        (to /usr/openwin/lib)
   c ~john bi         (to /home/john/bin)

You must have some favorite directories that you frequent often.  A good
tip is to define aliases to jump to those directories.  The ktools above
has several examples:

   ch    (c ~)        jumps to home directory
   c.    (c ..)       jumps to parent directory (similarly c.. c...)
   P     (c ~/Perl)   jumps to a favorite directory

I have aliases for almost all my favorite directories.

If you've ever worked in a large software development environment, you are
familiar with the practice that multiple versions of the software codes are
kept in various nodes: official, testing, development, etc.  These nodes
have almost identical directory structures under them.  The real names of
these nodes are normally stored in environment variables such as $OFC, $TST,
$DEV, etc., which are automatically set by some utility either at login or
when invoked manually.  You can define aliases such as:

   alias co='c $OFC'
   alias ct='c $TST'
   alias dev='c $DEV'

to facilitate moving around from one node to another.  Actually, if you
really want to be able to navigate efficiently in such a system, you need
additional tools that exploits the fact that these node have almost
identical structures.  (For example, to  from one directory under $DEV to
the corresponding directory under $OFC.  The usual way is "cd $DEV $OFC"
and a more efficient way is "n o" if you have defined the appropriate
command "n").  Those additional tools are rather specific to the actual
environment and I'll skip them.


Up, Up You Go (. .. ...)
========================

You have an alias X to jump to a favorite directory /abc/def/gh/ijkl/xyz.
But you only need to go to /abd/def/gh occasionally.  It is not worthwhile
to define an alias for that (besides, you are running out of letters in the
alphabet).

You can use 

   X .      (to go up to /abc/def/gh/ijk)
   X ..     (to go up to /abc/def/gh)
   X .. t   (to go up to /abc/def/gh and then down to "T@@@" or "t@@@")


Setting Directory Aliases (sd) and Jumping to Directory Aliases (d)
===================================================================

Assigning an alias to jump to a favorite directory is a good strategy if
you know that the directory will be a favorite directory for many years to
come.  I have a different strategy to deal with directories that may be
hot for only a short period of time (e.g. a short project).  You want to be
able to do something ON THE FLY (rather than having to add a line in .zshrc
and to source the file - for every existing window).  

If you prefer, you can also use this new strategy for more permanent
favorite directories.  However, the new method has the caveat of permitting
accidental overwriting of existing alias names. 

While you are in the directory to which you want to assign an alias
(because you anticipate that you will need to go back to that directory
many times in the next 2 weeks), type the command

   sd ALIAS_STRING

where ALIAS_STRING is any string you choose to represent the directory. You
can even use an existing directory name or command name and there will be
no conflict.  If a directory ALIAS using the same string already exists, it
will be overwritten. The string need not be related in any way to the
actual name of the directory, although that would help you to remember the
alias.  For example, you may use "kbin" for "~kwong/Bin", or "ow" for
"/usr/openwin".

Rule d1: As soon as the alias is set with "sd", it is available in any
^^^^^^^  windows, or subsequent logins.

There is no need to source any files, or logout and log back in again.  To
jump to the directory, type

   d ALIAS

We follow similar principles used in the design of "c".

Rule d2: You only need to type the beginning portion of the alias string
^^^^^^^  as long as it can be identified as the first one (lexicographical
         order).  Unlike "c", though, "d" is case SENSITIVE.

Rule d3: After the alias argument, "d" transmutes into the "c" command.
^^^^^^^

For instance, 

   d kb t        will go to "kwong/Bin/Test 
   d kb . p      will go to ~kwong/Perl
   
if "kbin" is the first directory alias that begins with "kb" and "Test" is
the first subdir under that that begins with "T", and "Perl" is the first
directory under ~kwong that begins with "P".


I Can't Remember All the Directory Aliases that I Set a Few Months Ago
======================================================================

If you type just "d" by itself, a list of all aliases will be displayed in
alphabetical order, and you are prompted to enter the one that you want to
jump to.

If you have too many aliases, you may want to use the variant

   dm STRING    (dir aliases matching STRING)

which will print a list of aliases that contain the string STRING either in
the alias or in the complete pathname of the directory.


Cleaning Up Directory Aliases
=============================

After a while, you would have created too many aliases and many of them are
already outdated.  I have a command "unsd" to unset the aliases, but it is
not very ideal.  Currently, the best way is to edit the file ~/.dirnames,
delete the lines that you don't need.  Then logout and log back in again
(any suggestion to get rid of these steps?).  The problem is that if you
use "d" in any window after you've edited the file, the original .dirnames
may be restored and the edited changes may be gone.


Caveats
=======

For slower machines, "c" or "d" with too many arguments can be a bit slow.
Speed is tolerable on my machine and many I have used.

If a file server is down and the directories that "c" or "d" need to go to
lie on that server, "c" or "d" may hang (ideally they should be able to
detect that something is wrong and exit gracefully).  ^C may be able to
abort the command.

**************************************************************************
vim: tw=75



Messages sorted by: Reverse Date, Date, Thread, Author