From decwrl!sun-barr!cs.utexas.edu!uunet!allbery Thu Aug 3 08:51:55 PDT 1989 Article 1001 of comp.sources.misc: Path: decwrl!sun-barr!cs.utexas.edu!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v07i109: kashe - change directory by menu Message-ID: <61771@uunet.UU.NET> Date: 29 Jul 89 00:54:46 GMT Sender: allbery@uunet.UU.NET Reply-To: jack@cs.glasgow.ac.uk Lines: 701 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 7, Issue 109 Submitted-by: jack@cs.glasgow.ac.uk Archive-name: kashe The following stuff saves me a LOT of typing directory names. Needs the Korn shell. I've been using it unchanged for months, but it's only been tested on Sun 3 machines. Much easier to use than to describe. RTFM before trying to use it - it helps to have 'lc', and your environment needs to be set up right. The details on using it if ksh isn't your login shell are there to help proselytize for ksh among users of other shells. best wishes - jack #--------------------------------CUT HERE------------------------------------- #! /bin/sh # # This is a shell archive. Save this into a file, edit it # and delete all lines above this comment. Then give this # file to sh by executing the command "sh file". The files # will be extracted into the current directory owned by # you with default permissions. # # The files contained herein are: # # -rw-r----- 1 jack 9439 Jul 28 19:10 kashe.1 # -rw-r----- 1 jack 6819 Jul 28 19:24 .kshenv # echo 'x - kashe.1' if test -f kashe.1; then echo 'shar: not overwriting kashe.1'; else sed 's/^X//' << '________This_Is_The_END________' > kashe.1 X.TH KASHE 1 "7 January 1989" "Jack Campin" "KSH(1) FUNCTIONS" X X.SH NAME X Xu, d, c, g, p, csort, cdrop, cwipe, csave, cload, cgrep, chelp \- quicker ways Xto change directory X X.SH SYNOPSIS X X.B u X.LP X.B d X[ X.I directory X] X.LP X.B c X[ X.I directory X] X.LP X.B g X.I number X.LP X.B p X.I pattern X.LP X.B csort X.LP X.B cdrop X.LP X.B cwipe X[ X.I pattern X] X.LP X.B csave X[ X.I file X] X.LP X.B cload X[ X.I file X] X.LP X.B cgrep X.I pattern X.LP X.B chelp X X.SH DESCRIPTION X XThis is a collection of Korn shell ( X.I ksh (1) X) functions I wrote after having to Xswitch between different directories a lot. I am a pretty hopeless typist, Xand even with X.I ksh's Xcommand-edit-and-retry features, it was getting boring Xentering something like X X.br X cd /bakcuppisa/users/jack/oscar/parse X Xand then having to change the "kc" to "ck". X XSo these functions give you a zippy menu interface to X.I cd. XTheir rationale is that most people most of the time only switch between a Xrelatively small "working set" of directories; these functions are intended to Xmake this set easily accessible. By far the most frequent commands in Unix are X.I cd, pwd, Xand X.I ls; Xthese functions improve the interface to all of these. I use them together Xwith a prompt line that displays my current directory, hence the "\ $ X" prompts below. The functions fall into two groups; the first maintains a Xcache of "interesting" directories, which you might use like this (assuming Xthe 3 directories below are in the cache already): X X.br X /pisa/users/jack $ c X.br X 1) /usr/spool/news/comp/binaries/mac X.br X 2) /usr/lib/tex X.br X 3) /backuppisa/users/jack X.br X 4) New... X.br X number? 1 X.br X /usr/spool/news/comp/binaries/mac $ X XThose directories might have got there by the following session, starting from Xempty: X X.br X /pisa/users/jack $ c X.br X directory? /usr/spool/news/comp/binaries/mac X.br X /usr/spool/news/comp/binaries/mac $ cd /usr/lib X.br X /usr/lib $ c tex X.br X /usr/lib/tex $ cd /backuppisa/users/jack X.br X /backuppisa/users/jack $ c X.br X 1) /usr/spool/news/comp/binaries/mac X.br X 2) /usr/lib/tex X.br X 3) New... X.br X number? 3 X.br X directory? . X.br X /backuppisa/users/jack $ cd X.br X /pisa/users/jack $ X XExtra items can be added at any time. Directories can be specified any way Xyou like, including tilde aliases. Directories in the cache are represented Xby absolute pathname. X.PP XThus X.B c Xhas three modes of operation - X.I cd Xto a cached directory by menu; add a new one to the cache when prompted and X.I cd Xto it; or add X.I directory Xto the cache and X.I cd Xto it, sidestepping the menu. Only valid directories can be added this way. XThus you can use it where you would previously have used X.I cd, Xto load your cache. X.PP XThere is also a "goto" for use when you know the menu item number you want X(typically, just after you've typed the wrong number to X.B c Xand still have the menu in front of you): X X.br X /pisa/users/jack $ g 2 X.br X /usr/lib/tex $ X XAnd a "goto by pattern" which scans the cache looking for a match: X X.br X /usr/lib/tex $ p mac X.br X /usr/spool/news/comp/binaries/mac $ X X.B p Xwill give you a menu if more than one directory in the cache matches the Xpattern. The pattern syntax is as for X.I egrep (1). XYou will usually be able to find a very short string that uniquely identifies Xyour target. I find this more useful than X.B g. X.PP X.B csort Xsorts the cache, eliminating duplicated entries. X.B cdrop Xdeletes directories from it; enter the numbers of the items to delete when Xprompted. X.B cwipe Xwill either clear it completely, or, if given a pattern argument, will just Xremove items matching that pattern. X.B csave Xsaves the cache to a file. X.B cload Xreloads it, appending the file to your current cache. X.PP XA less generally useful function in the same group, X.B cgrep, Xtakes a string and adds to the Xcache all accessible directories below your current directory whose pathnames Xhave a last component containing that string. It then gives you a menu Xlike X.B c. XIt's intended for finding your way around pieces of software Xthat scatter themselves in undocumented places all over the file system but use Xrelated names for all those places. You won't use this often, and it's slow X(because of the X.I ls -R Xembedded in it) but it will save you much frustration with X.I find Xand the like on the rare occasions when you want Xit. The string is an X.I egrep Xpattern. Since X.B cgrep Xembeds the string in a larger search pattern, some patterns will not work, in Xparticular those containing "^" or "$" as unescaped metacharacters, or "/". X.B cgrep X"" puts the entire tree below the current directory into the cache. X.PP XYou can ^C out of a menu selection leaving your directory unchanged. The Xcache is kept in a shell variable, so changes to it will not persist when Xreturning from a subshell unless you X.B csave Xbefore exiting and then X.B cload Xin the outer shell. X.PP XThe second group is a pair of functions that tries to emulate the way you move Xaround the file system on the Mac - a function X.B u Xto go up and X.B d Xto go down. (Originally inspired by my infuriatingly frequent typing of "cd.." Xfor "cd .."). It's usually a lot more convenient to use X.B d Xin "unknown territory" than to manually list subdirectories and then do X.I cd; Xfor one thing, X.B d Xfilters out non-directories from its display. If the target directory has no Xsubdirectories, X.B d Xacts like X.I cd. XThere are two alternative forms of X.B d Xprovided depending on whether the fast file lister X.I lc, X(written by gamiddleton@watmath.uucp and posted to comp.sources.unix in X1987) is available; comment out the one you don't want. X.PP XFinally, X.B chelp Xprints out a command synopsis. X.PP XBy using both groups of functions, you can move to any point in the file Xsystem without ever typing a directory name, and get back to it from anywhere Xwith four or five keystrokes. X X.SH ENVIRONMENT X XTo get all these into your environment, put them into a file called, say, X.I .kshenv. XThe Korn shell finds this file from the variable X.B ENV. X( X.B chelp Xuses X.B ENV Xto find the text it displays.) The file read or written by X.B cload Xand X.B csave Xis specified by the filename parameter if one is given; if not, by X.B KSHDIRFILE Xif that is set; and as a default, the file X.I .kshenvdirs Xin the home directory (this is OK if you always use the same domain). These Xfunctions use a shell variable X.B NL Xwhich should always contain a newline. This is simply to make the code easier Xto read. X.PP XHow you set up this environment depends on what Xyour login shell is. X X.PP X.B Korn shell: Xadd the lines X X.br X ENV=.kshenv \ \ export ENV X.br X KSHDIRFILE=~/.`domainname`-dirs \ \ export KSHDIRFILE X Xto your X.I .profile. XTo get the prompt line, also add X X.br X PS1='${PWD} $' \ \ export PS1 X.PP XTo preload the cache at login time, also add to your X.I .profile Xthe lines X X.br X .\ \ $ENV X.br X cload X X.PP X.B C shell: Xadd the lines X X.br X setenv ENV .kshenv X.br X setenv KSHDIRFILE \ \ ~/.`domainname`-dirs X Xto your X.I .login Xand add X X.br X PS1='${PWD} $' \ \ export PS1 X Xto your X.I .kshenv. X X.PP X.B Bourne shell: Xadd the lines X X.br X ENV=.kshenv \ \ export ENV X.br X KSHDIRFILE=~/.`domainname`-dirs \ \ export KSHDIRFILE X Xto your X.I .profile Xand add X X.br X PS1='${PWD} $' \ \ export PS1 X Xto your X.I .kshenv. X X.SH FILE FORMAT X XA cache file is simply a list of full pathnames of directories terminated by Xnewlines. X X.SH DIAGNOSTICS X XError messages are printed if these functions are called with the wrong number Xof parameters, if files can't be read or written, or if commands used by the Xfunctions fail. The exit code returned is 1 when a failure or a signal is Xdetected, otherwise 0. X X.SH BUGS X XIf you have more directories in the cache than will fit on the screen X(this might happen after using X.B cgrep X) you'll have problems seeing them with X.B c Xor X.B cdrop. XThere is no way to avoid this as far as I know. The workaround is to X.B csave, Xedit the file, and then X.B cload Xagain. X.PP XYou will sometimes get insane error messages if you enter random Xstrings instead of numbers. This is X.I ksh's Xfault and nothing can be done about it. X.PP XThese functions don't handle directory names with embedded newlines. Embedded Xspaces are OK, though; embedded tabs are invisible in the menus but otherwise Xseem to work (and if you embed tabs in directory names you deserve to have Xproblems, anyway). X.PP XIf anyone has bug reports or ideas for improvement, let me know. I suspect Xsymbolic links may not be handled quite right, but haven't found a definite Xbug there yet. The same ideas can be used for maintaining caches of strings Xin general; it would not be trivial to modify these into a general-purpose Xstring cache manager but might be worth doing someday. X.PP XAn undo facility would be easy enough to add; anyone want it? X X.SH KLUDGE X XYou need multiple cache files if you log in from more than one server, given Xthe inconsistencies in Glasgow's present NFS naming scheme - the same Xdirectory has pathname X.I /pisa Xfrom a login on vanuata and X.I /vanuata.pisa Xfrom a login on hawaii. Hence the need for the X.B KSHDIRFILE Xvariable, to say which is the right cache file for the domain you're logged Xin to. If you only ever use one domain you can ignore this. X X.SH SECTARIAN BRAG X XYou don't have a prayer of adapting these to the antediluvian C or Bourne Xshells while still getting useful speed. X X.SH AUTHOR X XJack Campin, Glasgow University Computing Science Department, 17 Lilybank XGardens, Glasgow G12 8QQ, Scotland (jack@cs.glasgow.ac.uk) ________This_Is_The_END________ if test `wc -c < kashe.1` -ne 9439; then echo 'shar: kashe.1 was damaged during transit (should have been 9439 bytes)' fi fi ; : end of overwriting check echo 'x - .kshenv' if test -f .kshenv; then echo 'shar: not overwriting .kshenv'; else sed 's/^X//' << '________This_Is_The_END________' > .kshenv X# ".kshenv" (or rename to whatever ENV is set to). X X# An assortment of functions for moving around the file X# system with fewer keystrokes, using a menu interface. X X# Jack Campin, 1988 X X## u - up X## d [directory] - down [from "directory"] X## c [directory] - cd to a cached (or new) directory by menu, X## or add "directory" to the cache and cd to it X## g number - cd to a cached directory by number rather than menu X## p pattern - cd to a cached directory containing "pattern" X## csort - sort directory cache, removing duplicate entries X## cdrop - remove entries from cache; enter number(s) when prompted X## cwipe [pattern] - empty the cache, or remove the items matching "pattern" X## csave [file] - write cache to a file, default $KSHDIRFILE or .kshenvdirs X## cload [file] - read cache from a file, default $KSHDIRFILE or .kshenvdirs X## cgrep pattern - cd to a menu-given directory below the current directory X## whose last component contains "pattern", adding all such X## directories to the cache X## chelp - print this synopsis X X# These use a cache of newline-separated directories in a variable KSHENVDIRS. X Xalias -t awk cat egrep lc ls sed sort tr # speeds things up a bit X XNL=" X" export NL # newline, used for IFS; variable used for readability X Xfunction c { X typeset ifs="$IFS" ps3="$PS3" X trap 'IFS="$ifs" ; PS3="$ps3" ; return 1' 1 2 3 4 5 6 7 8 14 15 X typeset j i val=1 ; IFS="$NL" X if [ $# != 0 ] X then j=$1 X else if [ "$KSHENVDIRS" != "" ] X then PS3="number? " X select i in $KSHENVDIRS New... X do [ "$i" = "New..." ] && break X test -n "$i" && cd $i || continue X IFS="$ifs" ; PS3="$ps3" ; return 0 X done X fi X read j?"directory? " X fi X if [ "$j" != "" ] # not interrupted while reading X then if eval cd $j # makes tilde substitution work X then # avoid introducing a duplicate X typeset x="$PWD" X for i in $KSHENVDIRS X do [ "$x" = "$i" ] && x="" && break X done X KSHENVDIRS="$KSHENVDIRS$x$NL" export KSHENVDIRS X val=0 X fi X fi X IFS="$ifs" ; PS3="$ps3" ; return $val X} X Xfunction g { X typeset ifs="$IFS" X trap 'IFS="$ifs" ; return 1' 1 2 3 4 5 6 7 8 14 15 X IFS="$NL" X if [ $# != 1 ] X then print "Usage: g " 1>&2 X else typeset -i n=0 ; typeset j X for j in $KSHENVDIRS X do (( n = $n + 1 )) X [ $n = $1 ] || continue X cd $j && IFS="$ifs" && return 0 X IFS="$ifs" ; return 1 X done X print "g: no directory number $1" 1>&2 X fi X IFS="$ifs" ; return 1 X} X Xfunction p { X [ $# != 1 ] && print "Usage: p pattern" 1>&2 && return 1 X [ "$1" = "" ] && print "p: null argument" 1>&2 && return 1 X typeset ifs="$IFS" ps3=$PS3 X trap 'IFS="$ifs" ; PS3="$ps3" ; return 1' 1 2 3 4 5 6 7 8 14 15 X typeset i val=1 dirs="`print "$KSHENVDIRS" | egrep "$1"`" ; IFS="$NL" X if [ "$dirs" = "" ] X then print "p: no match found for '""$1""'" 1>&2 X else set $dirs X if [ ${#*} = 1 ] X then cd $dirs ; val=0 X else PS3="number? " X select i in $dirs X do cd $i && val=0 && break X done X fi X fi X IFS="$ifs" ; PS3=$ps3 X return $val X} X Xfunction csort { X typeset ifs="$IFS" X trap 'IFS=" " ; return 1' 1 2 3 4 5 6 7 8 14 15 X IFS="$NL" X KSHENVDIRS=`print "$KSHENVDIRS" | sort -u`$NL export KSHENVDIRS X IFS="$ifs" X} X Xfunction cdrop { X [ "$KSHENVDIRS" = "" ] && print "cdrop: nothing to drop" 1>&2 && return 0 X typeset ifs="$IFS" ps3="$PS3" i X trap 'IFS="$ifs" ; PS3="$ps3" ; return 1' 1 2 3 4 5 6 7 8 14 15 X PS3="numbers to drop? " ; IFS="$NL" X select i in $KSHENVDIRS X do typeset -i m=0 ; typeset y="$KSHENVDIRS" X for m in `print $REPLY | tr ' \011' '\012'` # sidestep IFS X do typeset x="" ; typeset j ; typeset -i n=0 X for j in $y X do (( n = $n + 1 )) X if [ $m = $n ] X then x="$x@$NL" # tombstone value X else x="$x$j$NL" X fi X done X y="$x" X done X typeset tmp="" X for i in $y # edit out the tombstones X do [ $i != "@" ] && tmp="$tmp$i$NL" X done X KSHENVDIRS="$tmp" export KSHENVDIRS X IFS="$ifs" ; PS3="$ps3" ; break X done X} X Xfunction csave { X typeset x X case $# in X 0) if [ "$KSHDIRFILE" != "" ] X then x=$KSHDIRFILE X else x=~/.kshenvdirs X fi ;; X 1) x=$1 ;; X *) print "Usage: csave [filename]" 1>&2 ; return 1 ;; X esac X print "$KSHENVDIRS" > $x X} X Xfunction cload { X typeset x X case $# in X 0) if [ "$KSHDIRFILE" != "" ] X then x=$KSHDIRFILE X else x=~/.kshenvdirs X fi ;; X 1) x=$1 ;; X *) print "Usage: cload [filename]" 1>&2 ; return 1 ;; X esac X if test -r $x X then typeset i here="$PWD" tmp="$KSHENVDIRS" ifs="$IFS" X trap 'IFS="$ifs" ; return 1' 1 2 3 4 5 6 7 8 14 15 X IFS="$NL" X for i in `cat $x` X do cd "$i" && tmp="$tmp$i$NL" X cd "$here" X done X KSHENVDIRS="$tmp" export KSHENVDIRS X else print "cload: $x unreadable" 1>&2 ; IFS="$ifs" ; return 1 X fi X IFS="$ifs" X} X Xfunction cwipe { X case $# in X 0) KSHENVDIRS="" export KSHENVDIRS ; return 0 ;; X 1) typeset ifs="$IFS" ; IFS="$NL" X trap 'IFS="$ifs" ; return 1' 1 2 3 4 5 6 7 8 14 15 X typeset i j y="$KSHENVDIRS" tmp="" X for j in `print "$KSHENVDIRS" | egrep $1` X do typeset x="" X for i in $y X do if [ "$i" = "$j" ] X then x="$x@$NL" X else x="$x$i$NL" X fi X done X y="$x" X done X for i in $y # edit out tombstones X do [ "$i" = "@" ] || tmp="$tmp$i$NL" X done X KSHENVDIRS="$tmp" export KSHENVDIRS X IFS="$ifs" ;; X *) print "Usage: cwipe [pattern]" 1>&2 ; return 1 ;; X esac X} X Xfunction cgrep { X typeset ifs="$IFS" ps3="$PS3" X trap 'IFS="$ifs" ; PS3="$ps3" ; return 1' 1 2 3 4 5 6 7 8 14 15 X if [ $# != 1 ] X then print "Usage: cgrep pattern" 1>&2 ; return 1 X fi X typeset x="`ls -R | X sed -n '/:$/s/://p' | X egrep '(.*/)*.*'"${1}"'.*([~/])*$'`" X typeset i y="" here="$PWD" ; PS3="number? " IFS="$NL" X for i in $x X do cd $i && y="$y$PWD$NL" && cd $here X done X KSHENVDIRS="$KSHENVDIRS$y" export KSHENVDIRS X select i in $KSHENVDIRS X do cd $i && break X done X IFS="$ifs" ; PS3=$ps3 X} X Xfunction u { X cd .. X} X Xfunction d { X typeset ifs="$IFS" ps3="$PS3" tmp="$PWD" val=1 i X trap 'IFS="$ifs" ; PS3="$ps3" ; cd "$tmp" ; return 1' 1 2 3 4 5 6 7 8 14 15 X case $# in X 0) ;; X 1) cd "$1" || return 1 ;; # test if target is a directory X *) print "Usage: d [directory]" 1>&2 ; return 1 ;; X esac X PS3="number? " ; IFS="$NL" X select i in `lc -d1` X# slower alternative if "lc" is not available X# the gross pattern is (empirically) faster than the obvious one X# select i in `ls -al | X# sed -n '/^d.*/{ X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/[^ ]*[ ]*// X# s/^\.*$// X# p X# }'` X do [ "$i" != "" ] && cd $i && val=0 && break X done X IFS="$ifs" ; PS3=$ps3 ; return $val X} X Xfunction chelp { X sed -n '/^##/s/^##//p' < $ENV X} ________This_Is_The_END________ if test `wc -c < .kshenv` -ne 6819; then echo 'shar: .kshenv was damaged during transit (should have been 6819 bytes)' fi fi ; : end of overwriting check exit 0