views:

289

answers:

7

I'm trying to write some code in bash which uses introspection to select the appropriate function to call.

Determining the candidates requires knowing which functions are defined. It's easy to list defined variables in bash using only parameter expansion:

$ prefix_foo="one"
$ prefix_bar="two"
$ echo "${!prefix_*}"
prefix_bar prefix_foo

However, doing this for functions appears to require filtering the output of set -- a much more haphazard approach.

Is there a Right Way?

A: 

One (ugly) approach is to grep through the output of set:

set \
  | egrep '^[^[:space:]]+ [(][)][[:space:]]*$' \
  | sed -r -e 's/ [(][)][[:space:]]*$//'

Better approaches would be welcome.

Charles Duffy
In bash, "ugly" is on the left-hand of the sliding scale.
Kevin Little
+1  A: 

Use the declare builtin to list currently defined functions:

declare -F
Juliano
Helpful (though several other folks already suggested it), but not sufficient in and of itself; the output still needs to be processed into an array to be similar to `${!prefix_*}`, and adding `cut` to the command line isn't as clean as sticking to builtins.
Charles Duffy
+2  A: 

I have an entry in my .bashrc that says:

alias list='declare -F |cut -d" " -f3'

Which allows me to type list and get a list of functions. When I added it, I probably understood what was happening, but I can't remember to save my life at the moment.

Good luck,

--jed

Jed Daniels
+2  A: 
$ declare -F
declare -f ::
declare -f _get_longopts
declare -f _longopts_func
declare -f _onexit
...

So, Jed Daniel's alias,

declare -F | cut -d" " -f3

cuts on a space and echos the 3rd field:

$ declare -F | cut -d" " -f3
::
_get_longopts
_longopts_func
_onexit
Kevin Little
+1  A: 

Pure Bash:

saveIFS="$IFS"
IFS=$'\n'
funcs=($(declare -F))      # create an array
IFS="$saveIFS"
funcs=(${funcs[@]##* })    # keep only what's after the last space

Then, run at the Bash prompt as an example displaying bash-completion functions:

$ for i in ${funcs[@]}; do echo "$i"; done
__ack_filedir
__gvfs_multiple_uris
_a2dismod
. . .
$ echo ${funcs[42]}
_command
Dennis Williamson
Very nice, though the IFS twiddling on both ends makes things a little ugly. OTOH, the variant that comes off the top of my head to avoid it involve a subshell and string splitting (ie. `funcs=( $(IFS=$'\n'; funcs=($(declare -F)); echo "${funcs[@]##* }") )`), and which is certainly much more functional (as opposed to aesthetic) ugliness.
Charles Duffy
+1  A: 

How about:

compgen -A function   # compgen is a shell builtin
trevvor
Perfect! Simple, clean, and even allows prefix matching (as in: `compgen -A function prefix_`)
Charles Duffy
A: 

Perhaps my solution for this thread will work for you. Google for "get a list of function names in a shell script site:stackoverflow.com"

http://stackoverflow.com/questions/2630812/get-a-list-of-function-names-in-a-shell-script

Hai Vu
Actually, I think trevvor's solution in this thread is far better than any of those attached to that other question.
Charles Duffy
To be a little more clear on why the questions are different:This question is about going through the list of currently defined functions, not the list of functions defined in a specific script. Functions may be added by sourcing other scripts, or before the current script is invoked, so grepping does not help. The tool I wrote this code for is a library, so it can't make any assumptions about code structure, flow or organization.
Charles Duffy