In article <3944FF85.C6CBE...@odn.de>,
Heiner Steven <heiner.ste...@odn.de> writes:
> Dan Mercer <damer...@mmm.com> wrote:
> > Heiner Steven <heiner.ste...@odn.de> writes:
> [...]
> >> Is it possible to write Korn shell library functions that
> >> may be used as easily as perl modules?
> [...]
> >> In particular, I see the following open questions and problems
> >> regarding ksh libraries (kshlibs):
> >> o How can kshlibs be included?
> >> Should they be "dotted in" (. libfuncs.ksh) or is
> >> the "autoload" functionality more appropriate?
> >> [Perl: "use" or "require"]
> > The FPATH is more useful, since you only load what is necessary.
> > See below for a detailed explanation.
> Your excellent explanation describes in detail, how to use
> this feature, and it sounds like a feasible way to implement
> the libraries. The one disadvantage I see is, that the "package"
> or "module" concept is not supported: each function has to be
> in it's own source file, and it's not immediately clear, which
> functions belong together and establish a library. Do you have
> an idea how this could be accomplished? I suppose, that the FPATH
> approach does not support subdirectories (i.e. one subdirectory
> for each library).
FPATH can contain multiple libraries:
FPATH=~/fun:~/fun/ksh93:~/fun/dtksh
In addition, examine this section of the tutorial:
The file can contain additional commands including multiple function
definitions that will all be defined simultaneously.
For instance, I have a dtksh function library that creates and
manages the DtComboBox. All the functions are in one file,
DtComboBoxInitialize. The first function defined in the file
is DtComboBoxInitialize:
function DtComboBoxInitialize
{
[[ -o xtrace ]] && print -u2 "DtComboBoxInitialize entered"
DtLoadWidget DtComboBox dtComboBoxWidgetClass
}
As you can see, by itself it doesn't do much. But by invoking it,
it brings in all the functions defined in the file:
$ grep ^fun DtComboBoxInitialize
function DtComboBoxInitialize
function DtCreateComboBox
function DtComboBoxMenuPopup
function DtComboBoxAddItem
function DtComboBoxDeletePos
function DtComboBoxClearText
function DtComboBoxGetItems
> >> o How can name space conflicts be avoided?
> >> (i.e. if the calling script has a function named "error",
> >> an included library may define the function, too, causing
> >> problems).
> > First, by declaring functions with Korn syntax (function
> > keyword) and then using typeset, which declares local
> > variables.
> This works for local variables, but not for function names. If
> the main script declares a function, that is re-declared in a
> library module, a conflict occurs. Or is there a way to define
> "local" functions?
I don't know of anyway to define local functions. However, you
can conditionally define functions:
typeset +f funcname >/dev/null || function funcname {
...
}
> > Pass the variable names (vnames) of variables to be
> > set, like dtksh does, and use eval:
> That's an excellent idea! Parameter passing using "call by name"
> works, if the number of function return arguments is known by
> the caller in advance.
> This still leaves the problem, how multiple values (whose
> number is not known in advance) can be returned. I think the
> logical return value could be an array, but how can an array
> be returned from a function?
Don't think of using functions by:
x=$(funcname)
You might as well use a shell script for that. Think instead of
set_path PATH /usr/bin /usr/local/bin /usr/contrib/bin
set_path CDPATH . ~ /usr/local/bin
set_path MANPATH /usr/share/man /usr/local/man /usr/contrib/man
These are all scalars, but it works as well for arrays
array_from_string PATHARRAY PATH :
function array_from_string {
typeset vname=$1 string=$2
((3 == $#)) && typeset IFS="$3${IFS}"
eval set -A $vname -- \$$string
}
Since the name is not typeset in the function, it has global scope.
> >> o How should library functions return a value?
> >> Simple values or "scalar values" (i.e. a string or a number)
> >> could just be printed to stdout, i.e.
> >> welcomemsg=$(nntp_open $newsserver)
> >> But how should a function i.e. return an array of strings?
> >> set -A groupnames $(nntp_getgroups)
> >> would not preserver whitespace characters.
> > The most efficient way is as shown above.
> Well, as stated above the "call by name" way only works,
> if the number of expected return values are known in advance.
> >> [Perl: has comfortable array functions]
> >> And how could a function return more than one value, with
> >> a meaningful name?
> > Simply pass the vnames. In ksh93, a dot notation is used for
> > structured variables. Also, you have a name reference type variable
> > that eliminates the need, and dangers, of eval (never eval with
> > unknown input). For instance, this ksh93 function that turns
> > an array into a string:
> [ksh93 example removed]
> I agree that ksh93's "typeset -n" feature would be a very
> convenient one for "call by name", but I would recommend not
> to restrict the modules to ksh93, because it is not that far
> used than ksh88 is.
> > A ksh88 version:
> > function array_to_string
> > {
> > # Format: array_to_string vname_of_array vname_of_string
> separator
> > ((($# < 2) || ($# > 3))) && {
> > print -u2 " Format: array_to_string arrayname stringname
> [separator]"
> > return 1
> > }
> > typeset array=$1 string=$2
> > ((3==$#)) && [[ $3 = ? ]] && typeset IFS="${3}${IFS}"
> > eval $string='"${array[*]}"'
> > return 0
> > }
> Could you write an "string_to_array" function, that would preserve
> all special characters from the string, i.e.
> '"this is" "a multiword" "string with a $"'
I think you are mistaking the meaning of string to array. It takes
a single string, like the PATH, and parses it into an array, for
instance separating on the ":" character.
> resulting in
> array[0]="this is"
> array[1]="a multiword"
> array[2]="string with a \$"
> ?
> >> i.e. if a NNTP library function returns the number articles in
> >> a group, the first and the last article number, it would be
> >> very cryptic to assign the values to an array, i.e.
> >> set -A ginfo $(nntp_getgroup comp.unix.shell)
If nntp_getgroup is a function, you would call:
nntp_getgroup ginfo comp.unix.shell
> >> artcount=${ginfo[0]}
> >> firstart=${ginfo[1]}
> >> lastart=${ginfo[2]}
> >> [Perl: uses "hashes" or associative arrays, like "awk" has, i.e.
> >> ginfo{"count"}, ginfo{"first"}, ginfo{"last"}
> >> ]
> > ksh93 has associative arrays.
> IMHO your "call by name" example is even better suited for
> this problem.
> [...]
> > As promised, my function tutorial:
> ["FUN WITH FUNCTIONS (and aliases)" removed]
> Excellent summary!
> Heiner
> --
> ___ _
> / __| |_ _____ _____ _ _ Heiner STEVEN <heiner.ste...@odn.de>
> \__ \ _/ -_) V / -_) ' \ SHELLdorado for Shell Script Programmers:
>|___/\__\___|\_/\___|_||_| http://www.oase-shareware.org/shell/
Opinions expressed herein are my own and may not represent those of my employer.