【问题标题】:Get a list of function names in a shell script [duplicate]获取shell脚本中的函数名称列表[重复]
【发布时间】:2021-02-12 14:54:31
【问题描述】:

我有一个 Bourne Shell 脚本,其中包含多个函数,并允许通过以下方式调用:

my.sh <func_name> <param1> <param2>

在内部,func_name() 将被调用 param1param2

我想创建一个help 函数,它只列出所有可用的函数,即使没有参数。

问题:如何从脚本内部获取脚本中所有函数名的列表?

我想避免解析它并寻找函数模式。太容易出错了。

更新:代码。希望我的 help() 函数像 main() - 添加到代码中的函数会自动添加到帮助中。

#!/bin/sh

# must work with "set -e"

foo ()
{
    echo foo: -$1-$2-$3-
    return 0
}

# only runs if there are parameters
# exits
main ()
{
    local cmd="$1"
    shift
    local rc=0
    $cmd "$@" || rc=$?
    exit $rc
}

if [[ "$*" ]]
then
    main "$@"
    die "how did we get here?"
fi

【问题讨论】:

  • “伯恩壳”?我不知道有任何 Linux 发行版提供了开箱即用的Bourne,或者曾经有过; /bin/sh 几乎是通用的 POSIX sh(几十年新的标准,具有一组非零的不兼容性;例如,Bourne 将 ^ 视为管道字符,POSIX sh 没有;Bourne 使用 $[ ] 表示数学,POSIX sh使用$(( ))等)。
  • @CharlesDuffy 你有消息来源吗?
  • @OleTange,是哪一部分? (我当然是依靠“在 Linux 上”来排除诸如 SunOS / 旧 Solaris 之类的情况,其中 POSIX sh 位于不同的位置,但是(1)在我的评论中被引用,并且(2)问题是这样的标记)。
  • @OleTange, ..and re: $(( )),即使是 今天的 Heirloom Bourne 也不支持 $(( )) 进行数学运算。自己去heirloom.sourceforge.net/sh.html获取源码
  • ...也就是说,我误认为$[ ] 是伯恩主义。 Bourne 的正确做法是使用 expr 进行数学运算。

标签: linux shell sh


【解决方案1】:

您可以通过在自己的脚本上使用 grep 命令来获取脚本中的函数列表。为了使这种方法起作用,您需要以某种方式构造函数,以便 grep 可以找到它们。这是一个示例:

$ cat my.sh
#!/bin/sh

function func1() # Short description
{
    echo func1 parameters: $1 $2
}

function func2() # Short description
{
    echo func2 parameters: $1 $2
}

function help() # Show a list of functions
{
    grep "^function" $0
}

if [ "_$1" = "_" ]; then
    help
else
    "$@"
fi

这是一个交互式演示:

$ my.sh 
function func1() # Short description
function func2() # Short description
function help() # Show a list of functions


$ my.sh help
function func1() # Short description
function func2() # Short description
function help() # Show a list of functions


$ my.sh func1 a b
func1 parameters: a b

$ my.sh func2 x y
func2 parameters: x y

如果您不想在帮助中显示“私有”功能,则省略“功能”部分:

my_private_function()
{
    # Do something
}

【讨论】:

  • doxygen 的方式 :) 这很好,因为它允许打印描述,而不仅仅是函数名称。但是没有强制执行,一个被遗忘的评论就是一个被遗忘的功能。谢谢
  • 实际上,这看起来很简单。使用“函数”来区分公共和私有很好,并且对于我的目的来说足够强大
  • 如果您有兴趣,您的 eval 解决方案会弄乱参数中的空格,即 my.sh func1 "1 1" 2 3 将使用 $1=1 $2=1 $3=2 $4= 调用 func1 3 而不是 $1="1 1" 等。避免 eval 有帮助
  • n-alexander:感谢您提供有关删除 eval 的提示——这是不必要且不正确的。关于您的第一条评论:如果您忘记了某个函数的评论,它仍然会显示在帮助中。
  • 问题指定了“Bourne shell”。 function 关键字在 Bourne 中是不允许的,甚至在 POSIX sh 中也是不允许的;它只是一个 ksh 和 bash 扩展。
【解决方案2】:

typeset -f 返回带有函数体的函数,因此使用一个简单的 awk 脚本来提取函数名

f1 () { :; }
f2 () { :; }
f3 () { :; }
f4 () { :; }
help () {
    echo "functions available:"
    typeset -f | awk '/ \(\) $/ && !/^main / {print $1}'
}
main () { help; }
main

此脚本输出:

functions available:
f1
f2
f3
f4
help

【讨论】:

  • +1 表示可能bashkshzsh 中工作的干净解决方案:由于typeset -f 的输出在shell 中略有不同,因此awk 命令需要更灵活:typeset -f | awk '!/^main[ (]/ &amp;&amp; /^[^ {}]+ *\(\)/ { gsub(/[()]/, "", $1); print $1}'bash 中的小警告:如果您的环境中有 导出 函数,typeset -f 也会列出它们。
  • @glenn 有没有办法让 awk 命令也显示 cmets?
【解决方案3】:

你调用这个函数没有 争论,它吐出一个 “空格”分隔的列表 仅限函数名。


function script.functions () {
    local fncs=`declare -F -p | cut -d " " -f 3`; # Get function list
    echo $fncs; # not quoted here to create shell "argument list" of funcs.
}

将函数加载到数组中:


declare MyVar=($(script.functions));

当然,常识表明 任何尚未实现的功能 来源到当前文件之前 这被称为不会出现在 列表。

使列表只读并且 可用于导出到其他脚本 由该脚本调用:


declare -rx MyVar=($(script.functions));

将整个列表打印为换行符 分开:


printf "%s\n" "${MyVar[@]}";

【讨论】:

  • declare -F 仅(按预期)在bash 中有效,但在ksh(中断)中无效,在zsh 中无效(其中-F 声明一个浮点变量) .
  • 我怀疑以下内容会更便携并产生相同的结果:declare -f | grep '^.*\s()' | cut -f 1 -d ' '
【解决方案4】:

最好的办法是创建一个数组(您正在使用 bash),其中包含您想要宣传的函数,并让您的帮助函数遍历并打印它们。

单独调用set 将生成函数,但会生成它们的全部。你仍然需要解析以 () 结尾的东西来获得众所周知的符号

使用getopt 之类的东西将--function-name 转换为带有参数的function_name 也可能更明智。但是,好吧,理智是相对的,你还没有发布代码:)

您的另一个选择是创建一个loadable for bashset 的一个分支)来完成此任务。老实说,我更喜欢在为此任务编写可加载项之前先进行解析。

【讨论】:

  • @Tim +1 提出您的建议。 @n-alexander 我认为没有办法获取函数的元数据。你可以解析,但你不想那样做。
  • 您的意思是使用相同的数组来调用函数和打印它们?这就是我在 C 中所做的。在 shell 中,我希望能够只添加一个函数而不必更改更多代码。不过谢谢
  • @n-alexander - 除非在内部编写自己的 bash 以仅导出 'symbols' ,否则您将不得不对每个函数进行两次更改。我知道您似乎应该能够just get 这些函数,因为bash 知道它们,但是没有包含的接口可以这样做。你实际上让我想到了做一个可加载的来做到这一点。
猜你喜欢
  • 2010-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-30
  • 2019-01-09
相关资源
最近更新 更多