我正在围绕另一个定义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)
ing*_*net 14
进一步打高尔夫,copy_function并rename_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 1为return(返回当前状态代码)一旦copy_function定义,它就变得rename_function微不足道了.(只是不要重命名copy_function;-)
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)
非常好!
如果你只是想在名字前加一些东西,比方说orig_,那么我认为最简单的是
eval orig_"$(declare -f theirfun)"
Run Code Online (Sandbox Code Playgroud)
总结所有其他解决方案并部分纠正它们,以下是解决方案:
declare两次tail)但:
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%q是bash内置函数。)
当然,您可以将其拆分为复制+重命名,如下所示:
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)
| 归档时间: |
|
| 查看次数: |
5037 次 |
| 最近记录: |