I\'m after THE proper way to see if a function is defined or not. A POSIX compliant way.
__function_defined() {
FUNC_NAME=$1
d=$(declare -f $FUNCNAME
While I can understand why you might think so, dynamically pulling your script from another location is no great barrier to parsing (or "grepping") it as you like before you run it. Such a thing can be accomplished in several ways, but, as far as I can make out, they will all require a basic file descriptor for functionally complete operation.
Bash and its ilk provide us the convenience of transparent file descriptors via redirected sub-shelled commands which allow for all kinds of pipeline flexibility like this:
eval "$(wget --no-check-certificate -O - ${_URL} | tee \
>(read func; echo '_FUNC='"$(grep -Eo '^[^ |^#]*()' <(echo "${func}"))") \
| sudo sh)"
But, as has been mentioned already, such syntax devices are not specified by POSIX, and so you must find another way. Also, shorter though it may be, that can be a little confusing. Probably there are much better ways to do that, but I imagine them requiring 2 or 3 subshell levels at least. (It sure would be cool to be shown otherwise, though.)
What POSIX does specify, however, is the very useful heredoc
. The heredoc
is rare among declarable POSIX variable syntax types not only because it can neatly span multiple lines without escape kludges, or because it allows for clean definition of shell substitution by simply quoting its terminator, but most importantly because it describes a file type handle. This quality of the heredoc
is often overlooked, perhaps because Bash and co have largely obviated its importance for some time now, but we should be reminded of it every time we adhere to the convention of terminating a heredoc
with the "end of file" acronym EOF
.
In order to pull in a script file, modify it programmatically, and then execute it with a shell interpreter you will need a file descriptor. You can read and write to /tmp/
to allow for this, of course, but I generally try to avoid that because it just feels sloppy. You could even, perhaps, rewrite yourself if you must, but it is probably not the best habit to get into. If you find real cause to do this, however, the safest way is probably to define the rewrite code in a function at the beginning of your script and only call it at the very end so you can be sure the entire file has been fully loaded into memory. Something like this:
_rewrite_me() {printf %s "${1}" > ${0} && exec ${0}}
#SHELL CODE
#MORE
_NEW_ME="$(printf %s "${_MORE_SHELL_CODE}" | cat <${0})"
rewrite_me ${_NEW_ME}
Still, I like the heredoc
best, and I think it is the missing puzzle piece in this case. The following is made short and simple with use of the heredoc
, should be fully POSIX compliant, and should easily be adapted to your purposes, I hope:
#!/bin/sh
_URL='http://URL/TO/SCRIPT'
_SCRIPT="$(wget --no-check-certificate -O - ${_URL} |\
grep -Ev '^#!')"
_FUNC="$(sed -n 's:^\([^ |^#|^=]*\)().*{.*$:\1:p' <<_EOF_)"
${_SCRIPT}
_EOF_
echo "${_FUNC}"
sh <<_EOF_
${_SCRIPT}
_EOF_
if [ "$(command -v $FUNC_NAME)x" != "x" ]; then
echo " * INFO: Found function $FUNC_NAME"
return 0
fi
The question now, Is there a better solution?