Bash:如何从命令行一次调用中调用文件的所有函数?

Bash:如何从命令行一次调用中调用文件的所有函数?

我使用带有 Bash 的 Ubuntu 16.04,并且有一个包含 10 个函数的文件。每个函数本质上执行不同的任务。在每个函数的末尾,我这样称呼它:

x() {
    echo
}
x

这些调用向文件中添加了 10 行以上的行,出于美观原因,我希望从文件中保存这些行,因为无论如何我都会执行该文件中的所有函数(按照它们已经排序的顺序)。

当然,仅仅 source(在当前会话中执行)或 bashing(在子会话中执行)是不够的(很高兴,因为有时人们想要source/bash 不运行所有功能)。

对于这个小美学问题,我更喜欢的解决方案是在文件的来源/攻击之后,以某种方式直接从命令行调用所有函数。

在 Bash 中可能吗?

答案1

如果您只关心文件的行数,您可以执行以下操作:

my_func () {
    echo "Hello, world"
} && my_func

答案2

您可以使用:

source <(declare -f | grep -o '^[[:alnum:]]\+')

这列出了范围内的所有函数定义(declare -f),然后仅过滤掉它们的名称,这将是左边距(grep -o '^[[:alnum:]]\+')中唯一的字母数字字符串,最后获取结果输出,这是一个每行一个函数名称列表,通过流程替代这样它们都会在当前 shell 中被调用。

以上内容适用于grep具有 的GNU 或 BSD,但是如果需要,-o您可以换用合适的sed或命令。awk


这里的函数不会有任何特定的顺序(我认为,Bash 的哈希表顺序本质上是随机的)。如果您需要保留顺序,请适当命名它们并sort在中间添加一个:

source <(declare -f | grep -o '^[[:alnum:]]\+'|sort)

如果函数本身要独立调用,则这种命名结构可能是不可取的。我没有看到一个简单的解决方法。


这在单个文件中有效。如果您将文件导入到已经定义了函数的 shell 中,则需要记住哪些已经存在并跳过这些:

funcs=($(declare -f | grep -o '^[[:alnum:]]\+'|sort))
source file
source <(declare -f | grep -o '^[[:alnum:]]\+' | sort - <(printf '%s\n' "${funcs[@]}" | uniq -u)

有几种方法可以做到这一点,但所有这些方法都相当尴尬。当您重新定义现有函数时,这也会失败。另一种方法是grep从文件本身中取出所有函数定义行;如果你对如何编写它进行了结构化,那么它也是有效的。


如果您控制文件内容,可能更好的方法是在文件底部的单行数组定义中按照您希望调用的顺序列出函数名称。然后您可以在 source shell 中循环它并执行您需要的操作。

这种方法会更加清晰且不那么脆弱。然而,它确实引入了两个位置不同步的可能性。

答案3

如果你真的想要一句台词,我想出了这个,假设有一个文件scripts/1.sh

shopt -s extdebug; source scripts/1.sh; source="$_"; while IFS= read -r func; do declare_output="$(declare -F "${func##* }")"; [[ ${declare_output##* } == $source ]] && "${func##* }"; done < <(declare -F)

基本上,这用于declare确定环境中的哪些函数来自源文件并运行它们。我有三个函数,其中scripts/1.shecho Goodbyecruel、 和world.,并且该命令的输出是:

Goodbye,
cruel
world.

我不知道输出的顺序是否declare保证是函数的来源顺序,因此您可能必须适当地命名函数并使用sort.

答案4

出于各种原因,我建议保留显式调用。它们允许您暂时从调用列表中删除某些函数,更改顺序,在编辑时保留旧版本的函数,并具有仅调用其他函数(但不从“主”级别调用)的实用函数。最后一个似乎对于程序的正确构建尤其重要。


但如果你真的愿意,你可以这样做。尽管我可能建议至少为可自动运行的函数添加某种指示符。部分基于 @cas 的评论,这将运行# AUTORUN上面行中标记的所有函数。

#!/bin/bash

foo() { echo foo; }

# AUTORUN
bar() { echo bar; }

while IFS= read -r f; do
    $f
done < <(sed -nE '/^# *AUTORUN/!d; n; s/^([a-zA-Z0-9_]+) *\(\).*/\1/p' "${BASH_SOURCE[0]}")

可能需要当前形式的 GNU sed。

相关内容