3 # The psuedo-ksh autoloader.
6 # o One function per file.
7 # o File and function name match exactly.
8 # o File is located in a directory that is in FPATH.
9 # o This script (autoload) must be sourced in as early as possible. This
10 # implies that any code in this script should NOT rely on any library of local
11 # or self-defined functions having already been loaded.
12 # o autoload must be called for each function before the function can be used. If
13 # autoloads are in directories where there are nothing but autoloads, then
14 # 'autoload /path/to/files/*' suffices (but see options -a and -f).
15 # o The call must be made in the current environment, not a subshell.
16 # o The command line suffices as "current environment". If you have autoload
17 # calls in a script, that script must be dotted into the process.
19 # The first cut of this was by Bill Trost, trost@reed.bitnet.
20 # The second cut came from Chet Ramey, chet@ins.CWRU.Edu
21 # The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25
22 # The fourth cut came from Matthew Persico, matthew.persico@gmail.com 2017/August
24 autoload_calc_shimsize ()
26 echo $((AUTOLOAD_SHIM_OVERHEAD + 3 * ${#1}))
29 _autoload_split_fpath ()
31 (IFS=':'; set -- ${FPATH}; echo "$@")
48 while getopts xrvla:oyf opt; do
54 a) loadthese=$(find $OPTARG -maxdepth 1 -type f -printf '%f ');;
57 f) loadthese=$(find $(_autoload_split_fpath) -maxdepth 1 -type f -printf '%f ');;
58 *) echo "_aload: usage: _aload [-xrvlyf] [-a dir] [function ...]" >&2; return;;
64 [ -z "$loadthese" ] && loadthese="$@"
67 for func in $loadthese; do
69 exists_fn=$(declare -F $func)
70 if [ -n "$exists_fn" ] && ((doreload==0)) && ((doevalshim==0))
74 echo "autoload: function '$func' already exists"
82 funcfile=$(_autoload_resolve $func)
83 if [[ $funcfile ]] ; then
84 ## The file was found for $func. Process it.
87 ## For the first function loaded, we will not know
88 ## AUTOLOAD_SHIM_OVERHEAD. We can only calculate it after
89 ## we have loaded one function.
90 if [[ $AUTOLOAD_SHIM_OVERHEAD ]]; then
91 local size=$(wc -c $funcfile| sed 's/ .*//')
92 local shimsize=$(autoload_calc_shimsize $func)
93 if (( size <= shimsize)); then
95 andevaled=', optimized'
101 if ((doevalshim)); then
106 ## 'brand' as in branding a cow with a mark. We add a local
107 ## variable to each function we autoload so that we can tell
108 ## later on it is an autoloaded function without having to
109 ## maintain some bush array or hash that cannot be passed to
110 ## and used by subshells.
112 brandtext="eval \"\$(type $func | sed -e 1d -e 4ilocal\\ AUTOLOADED=\'$func\')\""
114 ## Don't bother trying to save space by shoving all the
115 ## eval text below onto one unreadable line; new lines will
116 ## be added at your semicolons and any indentation below
117 ## seems to be ignored anyway if you export the function;
118 ## look at its BUSH_FUNCTION representation.
121 local IS_SHIM="$func"
122 local file=$(_autoload_resolve '$func')
141 ((doexport)) && export -f $func && andexported=', exported' && ((exported+=1))
142 ((doverbose)) && echo "$func autoloaded${andexported}${andevaled}"
143 if [[ ! $AUTOLOAD_SHIM_OVERHEAD ]] && ((doshim)); then
144 ## ...we have just loaded the first function shim into
145 ## memory. Let's calc the AUTOLOAD_SHIM_OVERHEAD size
146 ## to use going forward. In theory, we could check
147 ## again here to see if we should optimize and source
148 ## in this function, now that we now the
149 ## AUTOLOAD_SHIM_OVERHEAD. In practice, it's not worth
150 ## duping that code or creating a function to do so for
152 AUTOLOAD_SHIM_OVERHEAD=$(type $func | grep -v -E "^$1 is a function" | sed "s/$func//g"| wc -c)
153 export AUTOLOAD_SHIM_OVERHEAD
156 echo "$func failed to load" >&2
161 ((summary)) && echo "autoload: loaded:$loaded exported:$exported optimized:$optimized overhead:$AUTOLOAD_SHIM_OVERHEAD bytes"
182 for func in $(declare | grep -E 'local\\{0,1} AUTOLOADED' | sed -e "s/.*AUTOLOADED=//" -e 's/\\//g' -e 's/[");]//g' -e "s/'//g")
184 if [ -n "$opt_p" ]; then echo -n "autoload "; fi
187 exported=$(declare -F | grep -E "${func}$" | sed 's/declare -f\(x\{0,1\}\).*/\1/')
188 [ "$exported" = 'x' ] && exported=' exported' || exported=' not exported'
189 executed=$(type $func | grep 'local IS_SHIM')
190 [ -z "$executed" ] && executed=' executed' || executed=' not executed'
192 echo "${func}${exported}${executed}"
198 if [[ ! "$FPATH" ]]; then
199 echo "autoload: FPATH not set or null" >&2
203 local p # for 'path'. The $() commands in the for loop split the FPATH
204 # string into its constituents so that each one may be processed.
206 for p in $( _autoload_split_fpath ); do
208 if [ -f $p/$1 ]; then echo $p/$1; return; fi
211 echo "autoload: $1: function source file not found" >&2
216 [ -z "$EDITOR" ] && echo "Error: no EDITOR defined" && return 1
221 local file=$(_autoload_resolve $func)
224 toedit="$toedit $file"
226 echo "$funcname not found in FPATH funcfile. Skipping."
230 [ -z "$toedit" ] && return 1
232 local timemarker=$(mktemp)
239 if [ $i -nt $timemarker ]
241 local f=$(basename $i)
250 [ -z "$PAGER" ] && echo "Error: no PAGER defined" && return 1
255 local file=$(_autoload_resolve $func)
258 topage="$topage $file"
260 echo "$funcname not found in FPATH funcfile. Skipping."
264 [ -z "$topage" ] && return 1
282 autoload [-xuremloyv] [function ...]
283 autoload -a directory [-oyv]
287 autoreload [function ...]
291 An implementation of the 'autoload' functionality built into other
292 shells, of which 'ksh' is the most prominent. It allows for a keeping
293 the process environment small by loading small 'shim' functions into
294 memory that will, on first call, load the full text of the given
295 function and run it. Subsequent calls to the function just run the
298 'autoreload' is a synonym for 'autoload -r'. See below.
302 o Each function to be autoloaded should be defined in a single file,
303 named exactly the same as the function.
305 o In order to avoid side effects, do NOT put code other than the
306 function definition in the file. Unless of course you want to do some
307 one-time initialization. But beware that if you reload the function
308 for any reason, you will rerun the initialization code. Make sure
309 your initialization is re-entrant. Or, better yet,
311 *** do NOT put code other than the function definition in the file ***
313 o These function definition files should be placed in a directory that
314 is in the FPATH environment variable. Subdirectories are NOT scanned.
316 o The autoload script should be sourced into the current process as
317 early as possible in process start up. See NOTES below for
320 o The calls to the autoload function must be made in the current
321 process. If your calls are in their own script, that script must be
322 sourced in. Command line invocations are also sufficient. (But see
325 o The first time the function is called, the shim function that was
326 created by the 'autoload' call is what is executed. This function
327 then goes and finds the appropriate file in FPATH, sources it in and
328 then calls the actual function with any arguments you just passed in
329 to the shim function. Subsequent calls just run the function.
333 -a Autoload (a)ll the functions found in the given directory.
335 -f Autoload all the functions found in all the directories on the
338 -p Print all the autoloaded functions.
340 -s Print all the autoloaded functions and add their export status.
342 -x Export the specified functions to the environment for use in
345 -u Unset the function, so it can be reloaded.
347 -r Reload the shims of the specified functions, even if the functions
348 have been already been executed. This will allow you to modify the
349 functions' source and have the new version executed next time the
352 It would be very easy to modify a function's script, run the
353 function and scratch your head for a long time trying to figure out
354 why your changes are not being executed. That's why we provide the
355 '-e' flag described below for modifications.
357 Reloads, of course, only apply in the context of the current session
358 and any future subshell you start from the current session. Existing
359 sessions will need to have the same 'autoload -r' command run in
362 -e Find the scripts in which the specified functions are defined and
363 start up \$EDITOR on those scripts. Reload the ones that were
364 modified when you exit \$EDITOR. (Note: If you use 'autoload -e foo'
365 to edit function 'foo', and then in your editor you separately load
366 up function 'bar', 'autoload' has no way of knowing that you edited
367 'bar' and will NOT reload 'bar' for you.)
369 Reloads, of course, only apply in the context of the current session
370 and any future subshell you start from the current session. Existing
371 sessions will need to have the same 'autoload -r' command run in
374 -m Find the scripts in which the specified functions are defined and
375 run \$PAGER on them ('m' is for 'more', because 'p' (page) and 'l'
376 (load) are already used as options in 'autoload').
378 -l When autoloading a function, eval the shim immediately in order to
379 load the true function code. See "Using '-l'" in the NOTES below for
382 -o Optimize. When autoloading, take the time to execute
384 'theCharCount=\$(wc -c \$theFuncFile)'
386 for each function and
388 if \$theCharCount < \$AUTOLOAD_SHIM_OVERHEAD
390 don't shim it, just eval directly.
392 -y Summar(y). Print the number of loaded, exported and optimized
395 -v Turns up the chattiness.
399 o Calling 'autoload' on a function that already exists (either shimmed
400 or expanded) silently ignores the request to load the shim unless it
401 has been previously removed (-u) or you force the reload (-r).
403 o Changing and reloading a function that has been exported does not
404 require it be re-exported; the modifications will appear in
405 subsequent subshells.
409 If you are running under set -x and/or set -v, you may see that the
410 shim does not appear to "work"; instead of seeing the shim first and
411 the real code subsequently, you may see the shim evaluated multiple
414 This may not be an error; review your code. What is most likely
415 happening is that you are calling the function in subshells via
416 backticks or $(), or in a script that is not being sourced into the
417 current environment. If you have not previously called the function
418 in question at your command line or in a script that was sourced into
419 the current environment, then the various subshells are going to
420 encounter the shim and replace with the real code before executing.
422 Remember, however, that environment modifications that occur in a
423 subshell are NOT propagated back to the calling shell or over to any
424 sibling shells. So, if you call an autoloaded function in a very
425 tight loop of very many subshells, you may want to make an 'autoload
426 -l' call before you start your loop. '-l' will instruct 'autoload' to
427 bypass the shim creation and just source in the function's file
428 directly. For a few calls, the overhead of repeatedly running the
429 shim is not expensive, but in a tight loop, it might be. Caveat
432 o Although the number of functions in the environment does not change
433 by using 'autoload', the amount of memory they take up can be greatly
434 reduced, depending on the size of your functions. If you have a lot
435 of small functions, then it is possible that the shim text will be
436 larger than your actual functions, rendering the memory savings moot.
438 'small' in this case can be determined by calling the function
439 'autoload_calc_shimsize' with the name of the function to determine
442 o In order to support the -p and -s options, we need a way to determine
443 if a function 'func' has been autoloaded or if it was loaded
444 diredctly. In order to do that, we modify the function's code by
447 local AUTOLOADED='func';
449 to the shim and to the actual function text, just after the opening
450 brace. Then supporting -p and -s is just a matter of grepping through
451 all the function text in memory. Even though grepping through the
452 environment may not be the most efficient way to support this, it is
453 the simplest to implement for -p and -s operations that are not
456 As a consquence of this (and other reasons), the AUTOLOAD* namespace
457 is reserved for autoloading. Make sure you check any functions that
458 you bring under autoload for use of variables or functions that start
459 with AUTOLOAD and change them.
461 o The easiest way to load shims for all functions on the FPATH is to run
465 in the profile that gets run for login shells.
467 When called in the profile of a login shell where no definitions
468 exist, -f will load all functions it can find on FPATH and -x will
469 export all of those functions to be available in subshells when this
470 is called in a login shell. Using this option will relieve you of the
471 need to call 'autoload' after Every Single Function Definition, nor
472 will you need to call it in subshells.
474 The only thing left to do is to load up the autoload function itself
475 and its helper functions. That needs to happen in your profile:
477 export FPATH=~/functions # or wherever you stash them
478 if [ -z $(declare -F autoload) ]
480 . ~/bin/autoload # or wherever you've put it
483 The 'if' statement is used to make sure we don't reload autoload
484 needlessly. Sourcing in the autoload script loads the 'autoload'
485 function and all of its support functions. Additionally, we export
486 all of these functions so that they are available in subshells; you
487 do not have to re-source the autoload file in '.bushrc'.
489 o Even with all of these shenanigans, you will find cases where no
490 matter how hard you try, your autoloaded functions will be
491 unavailable to you, even if you run 'autoload -x -f'. The typical
492 condition for this is starting up not a subshell, but a brand new
493 DIFFERENT shell. And the typical example of this is git extensions.
495 At the time of this writing, git extensions work by taking a command
496 'git foo' and looking for a file 'git-foo' on the path. 'git' then
497 executes 'git-foo' in a new shell - it executes your command in
498 /bin/sh. That's not a subshell of your process. It will not get your
499 exported shell functions. Ballgame over.
501 If you find that you want your functions to be available in such
502 circumstances, convert them back to plain old scripts, make sure they
503 are 'sh' compliant and take the read/parse hit every time they are
511 if (( $# == 0 )) ; then _autoload_dump; return; fi
513 local opt OPTIND OPTARG
516 while getopts psuema:yxrvlohf opt
519 p|s) dumpopt="$dumpopt -${opt}";;
520 u) shift $((OPTIND-1)); _autoload_remove "$@"; return;;
521 e) shift $((OPTIND-1)); _autoload_edit "$@"; return;;
522 m) shift $((OPTIND-1)); _autoload_page "$@"; return;;
523 x|r|v|l|y|f|o) passthru="$passthru -$opt";;
524 a) passthru="$passthru -$opt $OPTARG";;
525 h) _autoload_help; return;;
526 *) echo "autoload: usage: autoload [-puUx] [function ...]" >&2; return;;
533 _autoload_dump $dumpopt
535 _aload $passthru "$@"
544 ## When we source in autoload, we export (but NOT autoload) the autoload
545 ## functions so that they are available in subshells and you don't have to
546 ## source in the autoload file in subshells.
553 _autoload_split_fpath \
555 autoload_calc_shimsize \