我可以使用 bash 获取脚本文件中的所有函数吗?

我可以使用 bash 获取脚本文件中的所有函数吗?

如果我有一个文件:
dome.sh

$ls -l dome.sh
-rwxrwxr-x 1 user user 0 Aug  8 11:19 dome.sh

文件内容类似如下:

#!/bin/bash
function() {
    'work'
    }
another() {
    'different work'
    }
...
#30 functions later
yetANOTHERfunction () {
    'even more work'
    }

其中“#30 个函数后续”表示有 30 个独特的附加函数

我能否以某种方式遍历该脚本中的所有函数来识别它们?

就像是:

for i in $(get all functions in this file); do
    echo $i
done

生产:

function
another
...
yetANOTHERfunction

答案1

如果将“declare -F”中的“F”大写,那么它将返回类似“declare -f functionName”的行。现在您需要做的就是删除该行的第一部分。以下是代码:

#!/bin/bash
function () {
   return
}
another () {
   return
}
yetANOTHERfunction () {
   return
}
IFS=$'\n'
for f in $(declare -F); do
   echo "${f:11}"
done

答案2

尝试:

get_fcn_list () {
    env -i bash --noprofile --norc -c '
    source "'"$1"'"
    typeset -f |
    grep '\''^[^{} ].* () $'\'' |
    awk "{print \$1}" |
    while read -r fcn_name; do
        type "$fcn_name" | head -n 1 | grep -q "is a function$" || continue
        echo "$fcn_name"
    done
'
}

for fcn_name in $(get_fcn_list dome.sh); do
    echo "$fcn_name"
done

解释:

  • 您不需要自己解析函数,而是让我们bash解析它们(使用source)并转储内容(typeset -f)。

  • 该调用env -i bash --noprofile --norc旨在防止bash读取任何初始化文件。否则,您可能会得到在例如中定义的函数~/.bashrc

  • typeset -f将转储所有函数及其定义。每个定义都以以下内容开头:函数名称空间,,()空间新队.grepawk从这些行中提取函数名称。

  • 使用过滤器的原因是,当您具有以下功能时,可能会得到误报:

    f () 
    { 
        echo "
    a () 
    "
    }
    

    这里,while循环会错误地打印a,但过滤器会处理这个问题。

  • 编辑以从文件打印功能,并避免包含启动文件。

答案3

我尝试过这里提出的解决方案,但都有缺陷:

  • 我见过bashBedlam 的解决方案在对类似问题的几个回答中(例如这个),但是,尽管它们很受欢迎,但它们都是错误的,因为简单调用declare -F(或等效调用)不会列出脚本文件中明确声明的函数,它们还会列出从父 shell 导出的函数!需要一个干净的环境来防止外部声明干扰
  • Matei David 的解决方案考虑到环境的副作用(非常好),但不必要地复杂化(为什么要通过仅提供那些名称来过滤整个函数定义输出typeset -fdeclare -F);除此之外,它不会保护其输出免受源脚本可能的标准输出的影响。

这是我的解决方案(感谢 Matei David 提供env/bash call):

get_fcn_list () {
  env -i bash --noprofile --norc -c '
    source "'"$1"'" >/dev/null
    while read -r line; do
      echo ${line##* }
    done < <(declare -F)
    '
}

for fcn_name in $(get_fcn_list dome.sh); do
    echo "$fcn_name"
done

相关内容