include
# # Source file inclusion subsystem (i.e. "shell library") # # The inclusion subsystem allows for selective import of the # shell environment from small source file components, with # easy source file separation. # # Two new functions comprise the include subsystem: # # 'include': source all files in an include directory # 'require': source a single file from an include directory # # To convert existing kitchen-sink scripts: # # 1. break out into logically separate source files # 2. place related source files into named subdirectories # 3. use 'include' and 'require' within source files as needed # 4. in main executable script, source this file at the top # 5. then bring in source files with 'require' or 'include' # # Source files and their contents are available selectively # to each script, maintained separately and only included as # needed on a per-script basis. # # directories: # # Source files are placed in subdirectories of the parent # directory containing this file. Each of those # directories corresponds to an 'include'. # # files: # # Individual source files are searched for by name in any # of the directories, but only one match is allowed, so # they must be unique. # # Each sourced file should list its dependencies on other # source files at the top, and duplicate sourcing is # prevented. Interdependencies are brought in automatically # in the sourced files from their own use of 'require' # without the user having to know those details in the main # calling script, or know anything about the necessary # sourcing order. # # Ideally, each 'require' (i.e. source file) contains only a # single function, and each 'include' pulls in a set of # related functions, all grouped in one directory. However, # any ratio of functions to source files which fits the # situation (or form a logical unit) can be used. # ############################################################################## # so we can be used in different places without hardcoding a # base directory, we base everything relative to the # location of this file as first included from the toplevel # script # LIBSH_BASEDIR=${LIBSH_BASEDIR:-${BASH_SOURCE%*/*}} # we use some shell features which are probably not # available on some older systems # ((BASH_VERSINFO[0] < 2)) && return 1 # use this function to include individual source files by # name, in any subdirectory of the one this script is in # require () { local +i sourcefile local -a matched sourcefile=$1 (($# < 1)) && return 5 [[ $sourcefile =~ [^[:alnum:]_] ]] && return 10 # don't source it more than once, so sourced files # can themselves 'require' and 'include' each other; # unfortunately we have to pollute the environment # for this, as scanning arrays with strcmp() would be # too slow; we need a hash, and we aren't using bash4 # (yet!), so use the only hash available: the # environment # if compgen -A variable LIBSH_SOURCES_$sourcefile &>/dev/null then return else # XXX BASH WEIRDISM SPOOKY STUFF DESTROY USELESS BAGGAGE: # 1: using "declare" within functions makes it local ! # 2: without "eval" this breaks! even using ': var=""' ! eval LIBSH_SOURCES_$sourcefile= # just 'declare' breaks! fi # rely on shell globbing to find the source file for # us, but don't require the user to have any # particular glob settings (yes, this is needed) # matched=($(compgen -G "$LIBSH_BASEDIR/*/$sourcefile")) # we actually have a flat namespace for the moment, # despite the fact that we can group into separate # directories; goal is for 'include' and 'require' to be # as lightweight as possible # ((${#matched[@]} == 1)) || return 20 # at this point, all our sanity checks have concluded # successfully, so we go ahead and read the requested # sourcefile # source $matched || return 25 # recurse for any remaining arguments, to support # mutli-arg "require" lines, etc # shift if (($#)) then require $@ || return $? else return 0 fi } # source all files in a subdirectory of the one this script # is in when sourced # include () { local -i i local +i sourcedir local +i match local -a matched sourcedir=$LIBSH_BASEDIR/$1 (($# < 1)) && return 55 [[ $sourcedir =~ '[^[:alnum:]_/-\.]' ]] && return 60 test -d $sourcedir || return 65 matched=($(compgen -G "$sourcedir/*")) for ((i = 0; i < ${#matched[@]}; i++)) do match=${matched[i]} require ${match##*/} || return $? done } # vim:syn=sh:ft=sh