如何从 bash/zsh 中提取函数及其所有与 shell 相关的依赖项?

如何从 bash/zsh 中提取函数及其所有与 shell 相关的依赖项?

我编写了很多有用的小功能,有时我喜欢与朋友或全世界分享它们。问题是我在函数中使用了很多辅助函数和别名,并且我没有动力逐行阅读函数代码并找到所有依赖项。我非常确定这个过程可以自动化;我们首先需要对要导出的函数的代码进行标记(用于which func获取其代码),然后导出这些标记的任何别名,并对每个标记递归地重复此过程(执行 awhich token并查看它是否存在并且是 shell-相关(因此我们对二进制文件不执行任何操作,或者可以选择将所有二进制文件添加到 binaries.txt),然后重复)。

是否有适用于此用例的现有工具?

如果没有,我如何标记代码?

更新:我知道我建议的方法会导出无关的内容,并且不能保证它已导出所有依赖项,但它适用于我的代码。

更新:由于我的函数往往很简单,因此如果导出工具仅使用我的示例参数运行该函数并导出此特定运行所需的所有依赖项也是可以的。

答案1

shell 解释其代码的方式只能通过一些粗略的启发法来近似。例如,作为极端情况,无法知道:

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

功能bar依靠foo,bar1bar2函数上而不运行它(并且您需要强制运行通过所有代码路径)。

zsh确实有一个 shell 标记运算符:z参数扩展标志(也Q用于删除一级引用)。但这不是您想要的。

例如,在f() { foo "bar baz" "bar $(bar)"; },中将${(z)functions[f]}返回foo,"bar baz"并且"bar $(bar)",您将错过对该函数的调用bar

一种方法可能是提取看起来像典型函数名称的单词(在 中zsh,函数名称可以包含任何内容,但人们很少使用[[:alnum:]:._-]其中除了字符之外的任何内容),然后根据函数名称列表检查它们。

all_functions=(${(k)functions})
list-functions() (
  typeset -aU processed unprocessed=("$@") new list
  while (($#unprocessed)) {
    for f ($unprocessed) {
      autoload +X $f 2> /dev/null
      list=(${(s.:.)functions[$f]//[^[:alnum:]:._-]/:})
      new+=(${all_functions:*list})
      processed+=($f)
    }
    unprocessed=(${new:|processed})
  }
  print -rol -- $processed
)

_path_commands对完成系统的功能进行测试,我得到:

_all_labels
_alternative
_arguments
c
_cache_invalid
_call_program
_call_whatis
_command_names
_complete
_completers
_dates
_delimiters
_describe
_description
_dispatch
_files
_globflags
_globqual_delims
_globquals
_groups
_have_glob_qual
_history_modifiers
_ignored
_jobs
_list_files
_main_complete
_message
_next_label
_next_tags
_nm
_normal
_object_files
_parameters
_path_commands
_path_commands_caching_policy
_path_files
_pick_variant
_requested
_retrieve_cache
_services
_set_command
_setup
_store_cache
_suffix_alias_files
_tags
u
_users
_wanted

(运行functions $(list-functions _path_commands)以转储所有这些函数的定义)。

您可以在那里发现至少两个误报:cu, my 中的两个函数~/.zshrcc并且u可能在某些命令参数中用作变量名或参数扩展标志或单词,对此我们无能为力。

我不会太担心别名。别名在函数运行时不会扩展。它们在函数定义或加载时展开,但随后的输出functions将显示展开后的形式。

相关内容