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 de开发者_开发百科fined. 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?
How about compgen:
compgen -A function # compgen is a shell builtin
$ 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
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
zsh
only (not what was asked for, but all the more generic questions have been closed as a duplicate of this):
typeset -f +
From man zshbuiltins
:
-f The names refer to functions rather than parameters.
+ If `+' appears by itself in a separate word as the last
option, then the names of all parameters (functions with -f)
are printed, but the values (function bodies) are not.
Example:
martin@martin ~ % cat test.zsh
#!/bin/zsh
foobar()
{
echo foobar
}
barfoo()
{
echo barfoo
}
typeset -f +
Output:
martin@martin ~ % ./test.zsh
barfoo
foobar
Use the declare builtin to list currently defined functions:
declare -F
This has no issues with IFS nor globbing:
readarray -t funcs < <(declare -F)
printf '%s\n' "${funcs[@]##* }"
Of course, that needs bash 4.0.
For bash since 2.04 use (a little trickier but equivalent):
IFS=$'\n' read -d '' -a funcs < <(declare -F)
If you need that the exit code of this option is zero, use this:
IFS=$'\n' read -d '' -a funcs < <( declare -F && printf '\0' )
It will exit unsuccesful (not 0) if either declare
or read
fail. (Thanks to @CharlesDuffy)
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.
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
This collects a list of function names matching any of a list of patterns:
functions=$(for c in $patterns; do compgen -A function | grep "^$c\$")
The grep limits the output to only exact matches for the patterns.
Check out the bash command type as a better alternative to the following. Thanks to Charles Duffy for the clue.
The following uses that to answer the title question for humans rather than shell scripts: it adds a list of function names matching the given patterns, to the regular which
list of shell scripts, to answer, "What code runs when I type a command?"
which() {
for c in "$@"; do
compgen -A function |grep "^$c\$" | while read line; do
echo "shell function $line" 1>&2
done
/usr/bin/which "$c"
done
}
So,
(xkcd)Sandy$ which deactivate
shell function deactivate
(xkcd)Sandy$ which ls
/bin/ls
(xkcd)Sandy$ which .\*run_hook
shell function virtualenvwrapper_run_hook
This is arguably a violation of the Unix "do one thing" philosophy, but I've more than once been desperate because which
wasn't finding a command that some package was supposed to contain, me forgetting about shell functions, so I've put this in my .profile.
#!/bin/bash
# list-defined-functions.sh
# Lists functions defined in this script.
#
# Using `compgen -A function`,
# We can save the list of functions defined before running out script,
# the compare that to a new list at the end,
# resulting in the list of newly added functions.
#
# Usage:
# bash list-defined-functions.sh # Run in new shell with no predefined functions
# list-defined-functions.sh # Run in current shell with plenty of predefined functions
#
# Example predefined function
foo() { echo 'y'; }
# Retain original function list
# If this script is run a second time, keep the list from last time
[[ $original_function_list ]] || original_function_list=$(compgen -A function)
# Create some new functions...
myfunc() { echo "myfunc is the best func"; }
function another_func() { echo "another_func is better"; }
function superfunction { echo "hey another way to define functions"; }
# ...
# function goo() { echo ok; }
[[ $new_function_list ]] || new_function_list=$(comm -13 \
<(echo $original_function_list) \
<(compgen -A function))
echo "Original functions were:"
echo "$original_function_list"
echo
echo "New Functions defined in this script:"
echo "$new_function_list"
精彩评论