john@basser.cs.su.oz.AU (John Mackin) (04/13/88)
comp.sources.misc: Volume 2, Issue 93 Submitted-By: John Mackin <john@basser.cs.su.oz.AU> Archive-Name: autoload # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by john on Wed Apr 13 07:08:49 EST 1988 # Contents: README autoload which echo x - README sed 's/^@//' > "README" <<'@//E*O*F README//' @ Autoloading Shell Scripts as Functions @ Concept and Implementation by (in alphabetical order): @ John Mackin <john@basser.cs.su.oz.AU> @ Boyd Roberts <boyd@basser.cs.su.oz.AU> If you don't have a shell that supports functions, don't bother with this. If you do, well, we developed this under the Rob Pike (Eighth Edition) shell, but we tested it on System V (seems to work as long as you don't try to export any functions) and under ksh (seems like it might work, we aren't sure though). Caveat emptor, it's mainly the idea that matters, we're sure you can make it work on your implementation if you want to. Suppose you have a shell function that you don't often use, that must be a function and not a script (maybe it sets environment variables). Or just say you have some big functions that you would like to have around when you want them, but that don't clutter your environment when you're not using them (for those that can export functions, that is). Then this code is for you. Originally inspired by the Franz Lisp `autoload' mechanism, this allows you to write your functions as scripts, and have them autoloaded as functions into the calling shell the first time they are executed. For example, if $HOME/bin was in your path, and $HOME/bin/foo contained: @ #!/bin/sh @ echo This is foo. Then if you included our autoload functions in your .profile, along with a call to autoload: @ autoload foo foo would originally get a function definition that would cause its autoloading if it were ever used, and after it was first used it would be defined as: @ foo() @ { @ echo This is foo. @ } We've included a copy of `which' after Kernighan & Pike (`The UNIX Programming Environment') so that this can be 100% self-contained. Feel free to use a shell builtin instead if you have one that works (we do, almost...). One `gotcha' is that if you autoload a script that uses here documents (that is, `<<') you may be surprised by the result. It works on our shell, though; well, sort of. I have no idea what other shells will do with it. Another `gotcha' is that you shouldn't blindly autoload any script; rather, it requires a little bit of pre-planning. In particular, setting environment variables cuts both ways. What's great for a cutie that changes $TERM based on what kind of terminal your window is emulating is no fun at all if the script (and hence the function) changes PATH, or worse, IFS. Have fun! John Mackin, Basser Department of Computer Science, @ University of Sydney, Sydney, Australia john@basser.oz.AU (john%basser.oz.AU@UUNET.UU.NET) {uunet,mcvax,ukc,nttlab}!munnari!basser.oz!john @//E*O*F README// chmod u=rw,g=r,o=r README echo x - autoload sed 's/^@//' > "autoload" <<'@//E*O*F autoload//' autoload() { @ for i @ do @ eval "$i() @ { @ loadit $i && @ $i "'"$@"'" @ } @ export $i @ " @ done } loadit() { @ if cmd=`which $1` @ then @ eval "$1() @ { @ `sed 's/\([^A-Za-z0-9_]*\)exec[ ]/\1/ @ s/\([^A-Za-z0-9_]*\)exit[ ]/\1return / @ s/\([^A-Za-z0-9_]*\)exit$/\1return/ @ s/\([^A-Za-z0-9_]*\)trap[ ]/\1: /' $cmd` @ } @ " @ else @ echo $1: not found >&2 @ return 1 @ fi } export autoload loadit @//E*O*F autoload// chmod u=rw,g=r,o=r autoload echo x - which sed 's/^@//' > "which" <<'@//E*O*F which//' # which - which command will execute? oldpath=$PATH PATH=/bin:/usr/bin case $# in @ 1) ;; @ *) echo usage: `basename $0` command-name >&2; exit 2 ;; esac for i in `echo $oldpath | sed 's/^:/.:/ @ s/::/:.:/g @ s/:$/:./ @ s/:/ /g'` do @ if test -f $i/$1 @ then @ echo $i/$1 @ exit 0 @ fi done exit 1 @//E*O*F which// chmod u=rwx,g=rx,o=rx which exit 0