如何重命名bash函数?

Car*_*yer 30 bash function

我正在围绕另一个定义bash函数的软件包开发一些便利包装器.我想用我自己的同名函数替换他们的bash函数,同时仍然能够从我的内部运行他们的函数.换句话说,我需要重命名它们的函数,或者为它创建某种持久化别名,当我创建同名函数时不会修改它.

举一个我不希望工作的天真尝试的简短例子(事实上并非如此):

$ theirfunc() { echo "do their thing"; }
$ _orig_theirfunc() { theirfunc; }
$ theirfunc() { echo "do my thing"; _orig_theirfunc }
$ theirfunc
do my thing
do my thing
do my thing
...
Run Code Online (Sandbox Code Playgroud)

显然我不想要无限递归,我希望:

do my thing
do their thing
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

小智 39

这是一种消除临时文件的方法:

$ theirfunc() { echo "do their thing"; }
$ eval "$(echo "orig_theirfunc()"; declare -f theirfunc | tail -n +2)"
$ theirfunc() { echo "do my thing"; orig_theirfunc; }
$ theirfunc
do my thing
do their thing
Run Code Online (Sandbox Code Playgroud)

  • 辉煌!谢谢,bash向导;-) (2认同)
  • 另一种处理递归函数的可能性:`eval'$(declare -f theirfunc | sed's /\btheirfunc\b/orig_theirfunc/g)"` (2认同)

ing*_*net 14

进一步打高尔夫,copy_functionrename_function发挥作用:

copy_function() {
  test -n "$(declare -f "$1")" || return 
  eval "${_/$1/$2}"
}

rename_function() {
  copy_function "$@" || return
  unset -f "$1"
}
Run Code Online (Sandbox Code Playgroud)

从@Dmitri Rubinstein的解决方案开始:

  • 无需拨打declare两次电话.错误检查仍然有效.
  • func使用_特殊变量消除temp var().
    • 注意:使用test -n ...是唯一可以保留_并且仍然能够返回错误的方法.
  • 更改return 1return(返回当前状态代码)
  • 使用模式替换而不是前缀删除.

一旦copy_function定义,它就变得rename_function微不足道了.(只是不要重命名copy_function;-)

  • `rename_function"a b"`由于缺少引用而做了一些非常意想不到的事情.建议修复:`copy_function(){test -n"$(declare -f $ 1)"&& eval"$ {_/$ 1/$ 2}"; }; rename_function(){copy_function"$ @"&& unset -f"$ 1"; }`(但这并不能解决所有可能的误用,但大多数情况下) (2认同)

Car*_*yer 13

啊哈.找到了一个解决方案,虽然它不是真的很漂亮:

$ theirfunc() { echo "do their thing"; }
$ echo "orig_theirfunc()" > tmpfile
$ declare -f theirfunc | tail -n +2 >> tmpfile
$ source tmpfile
$ theirfunc() { echo "do my thing"; orig_theirfunc; }
$ theirfunc
do my thing
do their thing
Run Code Online (Sandbox Code Playgroud)

我确信这可以通过真正的bash向导来改进.特别是放弃对tempfile的需求会很好.

更新:bash向导Evan Broder迎接挑战(见上面接受的答案).我将他的答案重新表述为通用的"copy_function"函数:

# copies function named $1 to name $2
copy_function() {
    declare -F $1 > /dev/null || return 1
    eval "$(echo "${2}()"; declare -f ${1} | tail -n +2)"
}
Run Code Online (Sandbox Code Playgroud)

可以像这样使用:

$ theirfunc() { echo "do their thing"; }
$ copy_function theirfunc orig_theirfunc
$ theirfunc() { echo "do my thing"; orig_theirfunc; }
$ theirfunc
do my thing
do their thing
Run Code Online (Sandbox Code Playgroud)

非常好!

  • Bash继承!我从没想过我会看到这一天. (9认同)

par*_*hed 7

如果你只是想在名字前加一些东西,比方说orig_,那么我认为最简单的是

eval orig_"$(declare -f theirfun)"
Run Code Online (Sandbox Code Playgroud)

  • 看起来很简单:-) (2认同)

Tin*_*ino 6

总结所有其他解决方案并部分纠正它们,以下是解决方案:

  • 不使用declare两次
  • 不需要外部程序(如tail
  • 没有意外的替代品
  • 相对较短
  • 通过正确的引用,可以保护您免受常见的编程错误的影响

但:

  • 它可能不适用于递归函数,因为副本中用于递归的函数名称不会被替换。正确地进行这样的更换是一项过于复杂的任务。如果您想使用此类替换,您可以尝试这个答案/sf/answers/1318769021/eval "${_//$1/$2}"而不是(eval "${_/$1/$2}"注意 double //)。然而,替换名称对于太简单的函数名称(例如)会失败,并且对于计算的递归(例如)a会失败command_help() { case "$1" in ''|-help) echo "help [command]"; return;; esac; "command_$1" -help "${@:2}"; }

一切结合起来:

: rename_fn oldname newname
rename_fn()
{
  local a
  a="$(declare -f "$1")" &&
  eval "function $2 ${a#*"()"}" &&
  unset -f "$1";
}
Run Code Online (Sandbox Code Playgroud)

现在测试:

somefn() { echo one; }
rename_fn somefn thatfn
somefn() { echo two; }
somefn
thatfn
Run Code Online (Sandbox Code Playgroud)

按要求输出:

two
one
Run Code Online (Sandbox Code Playgroud)

现在尝试一些更复杂的情况,它们都给出了预期的结果或失败:

rename_fn unknown "a b"; echo $?
rename_fn "a b" murx; echo $?

a(){ echo HW; }; rename_fn " a " b; echo $?; a
a(){ echo "'HW'"; }; rename_fn a b; echo $?; b
a(){ echo '"HW"'; }; rename_fn a b; echo $?; b
a(){ echo '"HW"'; }; rename_fn a "b c"; echo $?; a
Run Code Online (Sandbox Code Playgroud)

有人可能会说以下仍然是一个错误:

a(){ echo HW; }; rename_fn a " b "; echo $?; b
Run Code Online (Sandbox Code Playgroud)

因为它应该失败,因为" b "这不是正确的函数名称。如果你真的想要这个,你需要以下变体:

rename_fn()
{
  local a
  a="$(declare -f "$1")" &&
  eval "function $(printf %q "$2") ${a#*"()"}" &&
  unset -f "$1";
}
Run Code Online (Sandbox Code Playgroud)

现在这也捕获了这个人为的案例。(请注意printfwith%qbash内置函数。)

当然,您可以将其拆分为复制+重命名,如下所示:

copy_fn() { local a; a="$(declare -f "$1")" && eval "function $(printf %q "$2") ${a#*"()"}"; }
rename_fn() { copy_fn "$@" && unset -f "$1"; }
Run Code Online (Sandbox Code Playgroud)

我希望这是101%的解决方案。如果需要改进,请评论;)


小智 5

可以使用shell参数扩展而不是tail命令来改进copy_function:

copy_function() {
  declare -F "$1" > /dev/null || return 1
  local func="$(declare -f "$1")"
  eval "${2}(${func#*\(}"
}
Run Code Online (Sandbox Code Playgroud)