3

EDIT: I realized I was mistaken about a central assumption in this post: The ${!prefix@} syntax isn't able to get function names, but only variables. Therefore, there is no way to get ${!prefix@} to return function names, even if they used underscores. Thus, the answer to the bolded question I have below is a definitive "No." Still, Edgar's answer below is quite helpful for those who would like to get a list of function names based on a certain prefix.

Original question

The bash parameter expansion manual gives us a way to obtain a list of all variables (NOT functions) in the current environment that begin with some prefix:

${!prefix@}

I have a bunch of functions that all start with foo-, such as foo-setup, foo-run, foo-initialize, etc. Now, I've tried and failed to get ${!foo-@} and variations on it to return a list of functions starting with foo-. I've tried ${!foo\-@}, "${!foo\-@}", "${!foo-@}", bar=foo; ${!${bar}-@}, and many other variants. I know that the hyphen - has its own meaning in parameter expansion, so it's expected that, without some escaping or the like, it wouldn't work. But I'd also expect that Bash should have mechanisms for escaping special characters in any context, including during parameter expansion, unless ... perhaps that's not the case here?

(I've looked into the fact that, if I want cross-platform compatibility, I should stay clear of hyphenated function names. I would agree with that. But also, it is so much smoother to type functions with hyphens instead of underscores...)

So, in the spirit of inquiry, I pose the question: Is there any way to get a list of defined functions (or namerefs in general) that all start with a hyphenated prefix using native parameter expansion? Perhaps the solution is escaping the hyphen in a way I didn't know?

(If not, is there an alternative method, perhaps by getting the full list of variables, then filtering them to leave only the ones with the desired hyphenated prefix?)

Q Z
  • 133

2 Answers2

2

Is there an alternative method?

Yes, you can use declare or compgen to get the list of defined functions:

Solution 1 (it will match functions that contains the specified string/pattern)

declare -F | grep -o 'foo-.*'
#or
compgen -A function | grep 'foo-'

Solution 2 (especial for matching prefix/starts with certain string/pattern)

declare -F | cut -d ' ' -f3 | grep '^foo-'
#or
compgen -A function | grep '^foo-'

About using parameter expansion, I'm not sure if it's possible to get the function names. According to the manual, it seems only works for variables:

${!prefix*}

${!prefix@}

Expands to the names of variables whose names begin with prefix, separated by the first character of the IFS special variable. When ‘@’ is used and the expansion appears within double quotes, each variable name expands to a separate word.

And what you said:

Admittedly, if I renamed all of my functions such that their foo- prefixes are changed to foo_, then ${!foo_@} does get me all of the functions.

I would like to know if you've tried and it worked (I don't think), because I tested it with some functions and didn't work.

Certainly you cannot define variables using a hyphen -, therefore it would not be possible to use ${prefix-@} or ${prefix-*}.

  • Yes, you are right! I realized that I was testing the parameter expansion on variable names. I was also unable to get it to work on function names. I've edited my post to remove that mistaken paragraph ("Admittedly,..."). – Q Z Sep 24 '22 at 02:45
  • I found the compgen method works well for my purposes: it directly produces a list of function names, which I can then promptly call 'unset -f' on, which is what I wanted to do. – Q Z Sep 24 '22 at 02:48
  • 1
    @QZ great!! glad this works for you :). And if you want too get only the function names with declare, you can use: declare -F | grep -o 'foo-.*' or declare -F | grep 'hello-*' | cut -d ' ' -f3 (it's still faster too use compgen). – Edgar Magallon Sep 24 '22 at 03:08
2

For completeness, if you wanted to get the list of function names that match a pattern in zsh, you'd do:

typeset +f -m 'foo-*'

(replace +f with -f to also see the definition of those functions).

However note that function names in zsh can be any sequence of bytes (after all they're in the same namespace as commands and file paths can be any non-empty sequence of non-null bytes), so that output is not post-processable reliably.

You can however get the list in an array, by looking for the keys of the special $functions associative array that Match the pattern instead:

foo_functions=( ${(Mk)functions:#foo-*} )

Example with weirdly named functions:

$ typeset +f -m 'foo-*'
foo-a
b
c
foo-barbaz
$ print -rC1 -- ${(q)foo_functions}
foo-a$'\n'b$'\n'c
foo-bar$'\0'baz