3

当我的 bash 脚本开始变得复杂时,我通常会将它们分解为函数。这尤其适用于复杂的管道,因为一系列复杂的管道命令(例如包含 while 循环)会很快变得难以阅读。当需要并行化时更是如此,其中 xargs 非常有帮助。

我知道我可以使用 将函数导出到子shell export -f,因此在一个简单的情况下我可以做到

export -f myfunction 
some-command | xargs -Iline bash -c "myfunction 'line'"

但是,如果myfunction依赖于其他函数,这将变得难以维护——每次函数发生变化时,子shell 执行myfunction更改所需的函数时,导出语句都必须更改——这似乎很容易出错。

有没有一些通用的方法来导出函数供子shell使用?我正在考虑类似于“导出所有定义的函数”命令的东西,然后它允许像这样的代码结构

main() { ... }
func1 () { ... }
func2 () { ... }
<export all functions>
main "$@"
4

4 回答 4

3

您的问题仅涉及导出功能。这在 bash 中很容易,见下文。

您的问题标题/主题暗示在 xargs 中使用函数,就好像它们是脚本一样;我不知道 xargs 可以直接“调用” bash 函数,但您当然可以将导出函数的使用包装在由 调用的脚本中xargs,见下文。

首先,列出函数的函数。默认用户函数和 -v 列出所有函数:

lsfns () {
   case "$1" in
      -v | v*)
         # verbose:
         set | grep '()' --color=always
         ;;
      *)
         declare -F | cut -d" " -f3 | egrep -v "^_"
         ;;
   esac
}

接下来是导出所有用户功能的功能:

exportfns () { export -f $(lsfns); }

或者只是放入export -f $(lsfns)你的.bashrc.

示例脚本 doit.sh:

#!/bin/bash
lsfns "$@" # make use of function exported by parent shell :)

命令行示例(在 之后chmod a+rx doit.sh):

echo -v | xargs doit.sh

与之比较

echo "" | xargs doit.sh

编辑1:进一步回应下面的kdb评论/答案(“遇到导出功能根本不起作用的情况”):

shell 函数的导出与 Posix 不兼容 - 即它仅适用于 Bash 以及可能的其他 shell,例如 Zsh、Ksh 等。

也就是说,在 Dash 和不提供“export -f”的“标准”Posix shell 中,我们无法导出函数,并且如果我们在 Bash 中导出函数,则运行以 sh-bang 开头的脚本,例如“#! /bin/dash”,该脚本将无法使用父 shell 中的“导出”函数,因为 Dash 无法识别通过 Bash 导出到“进程环境”的函数。


编辑 2:进一步回应 OP 评论“但如果myfunction依赖于其他功能,这将变得难以维护”:

这可能是一种可以充分利用 shellsource命令(别名“.”)的情况,例如:

. ~/etc/my-functions.sh; myMain ...

同样,如果您“生活”在函数中而不是脚本文件中,例如通过myMain在需要时调用,那么该函数的第一行可以为您的函数库提供源代码;

因为在“定期运行脚本”的情况下这将是额外的开销,所以myMain成为命令行存根函数,它(重新)加载您的函数库,并调用该actuallyDoit函数(如果您也可以从脚本内部调用该函数)有一个脚本文件)。

享受
泽南

于 2013-12-13T12:35:15.300 回答
1

这似乎可以打印所有函数名称。感觉很脆弱,所以测试一下

declare -f | grep -oP '^\S+(?=\s*\(\))'
于 2013-07-19T15:57:44.330 回答
0
export -f $(compgen -A function)
于 2015-02-26T19:03:13.980 回答
0

自从收到答案后,我发现许多情况下证明了一种不同的技术更可取:让脚本自行调用。一个简单的例子是

# Print hash and disk usage for each argument
if [[ $1 == run ]]; then
  shift 1
  printf "%s\0" "$@" | xargs -0 -n 1 -P "$NUMBER_OF_PROCESSORS" -- "$0" printpar
elif [[ $1 == printpar ]]; then
  echo ":: $(cat "$2" | sha1sum) $(du -sh "$2")"
else
  echo "Invalid first parameter '$1'"
  exit 1
fi

在现实世界的例子中,我要么对参数做出假设(例如__SUCH__,对自调用关键字使用形状),要么将递归调用隐藏在未记录的--command-line-switch.

导出函数通常更优雅,但对于大量函数,它可能会变得非常缓慢,我记得遇到了问题,它完全失败了。

于 2017-09-21T11:27:36.017 回答