I write a lot of little useful functions, and sometimes I like to share them with friends or the world. The problem is that I use a lot of my helper functions and aliases in my functions, and I don't feel motivated to read the function code line by line and find all of the dependencies. I am pretty sure this process can be automated; We first need to tokenize the code of the function we want to export (using which func to get its code), then export any aliases of these tokens, and repeat this process recursively for each token (do a which token and see if it exists and is shell-related (so we do nothing for binaries, or perhaps optionally add all binaries to a binaries.txt), and repeat).

Are there any existing tools for this use case?

If not, how do I tokenize the code?

Update: I know my suggested method exports extraneous things and doesn't guarantee that it has exported all the dependencies, but it works for my code.

Update: Since my functions tend to be simple, it is also okay if the exporting tool just runs the function with my sample arguments and exports all dependencies that were needed for this particular run.

  • 1
    Now, how about a function defined as f1() { eval 'f2 foo'; }, or f1() { "$1" foo; }; f1 f2. How are you going to find out f1 actually depends on f2? – Stéphane Chazelas Dec 11 '18 at 21:54
  • @StéphaneChazelas I planned to take tokens out of strings, too, since I do use strings for eval a lot. The second example is not causing a problem. If f1 f2 is in the code, then f2 will be exported, if not, we don't need to support it. – HappyFace Dec 11 '18 at 21:59
  • @StéphaneChazelas I know this approach exports a lot of unnecessary stuff, but that's a tradeoff I am willing to accept. – HappyFace Dec 11 '18 at 22:01
  • @StéphaneChazelas I just noticed that indeed this approach can be fooled by say using a function like evil() { eval "$1""$2"}; evil long func, but I don't do these kinds of stuff. This is a very practical question. :D – HappyFace Dec 11 '18 at 22:28
  • You can't just do string matching. A function could mention f2 in a string without this being related to a previously defined function f2. What you are looking at doing is implementing a full shell parser. – Kusalananda Dec 12 '18 at 9:54

The way shells interpret their code, that can only be approximated with some crude heuristics. For instance as an extreme case, there's no way to know that in:

foo() { "${0+b}$(echo AR | tr '[:upper:]' '[:lower:]')$1"; }
bar() { for i in 1 2; do foo "$i" "$i"; }

the bar function depends on the foo, bar1 and bar2 functions without running it (and you'd need to force the running to go through all code paths).

zsh does have a shell tokenising operator: the z parameter expansion flag (also Q to remove one level of quoting). But that's not what you want here.

For instance, in f() { foo "bar baz" "bar $(bar)"; }, ${(z)functions[f]} would return foo, "bar baz" and "bar $(bar)", you would miss the call to the bar function.

One approach could be to extract words that do look like typical function names (in zsh, function names can contain anything, but people rarely use anything but [[:alnum:]:._-] characters in them) and check them against the list of function name.

list-functions() (
  typeset -aU processed unprocessed=("$@") new list
  while (($#unprocessed)) {
    for f ($unprocessed) {
      autoload +X $f 2> /dev/null
  print -rol -- $processed

Tested on the _path_commands function from the completion system, I get:


(run functions $(list-functions _path_commands) to dump the definition of all those functions).

You can spot at least two false positives in there: c and u, two functions in my ~/.zshrc. c and u were probably used as variable names or parameter expansion flags or words in some command arguments, little we can do about that.

I wouldn't worry too much about aliases. Aliases are not expanded at run time in functions. They are expanded at function definition or loading time, but then the output of functions would show the expanded form.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.