2009-07-29 59 views
25

我正在开发另一个定义bash函数的软件包的便利包装。我想用我自己的同名命名函数替换它们的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 
... 

很显然,我不想无限递归,我想:

do my thing 
do their thing 

我该怎么做?

回答

37

这里是消除临时文件的方式:

$ 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 
+2

太棒了!谢谢你,bash向导;-) – 2009-09-02 18:14:05

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 

我敢肯定,这可以通过一个真正的bash向导得到改善。特别是它可以很好地满足临时文件的需要。

更新:bash向导埃文布罗德上升到挑战(见上面接受的答案)。我重新他的回答到一个通用的 “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)" 
} 

可以像这样使用:

$ theirfunc() { echo "do their thing"; } 
$ copy_function theirfunc orig_theirfunc 
$ theirfunc() { echo "do my thing"; orig_theirfunc; } 
$ theirfunc 
do my thing 
do their thing 

很不错的!

+7

Bash继承!我从来没有想过我会看到一天。 – shuckster 2009-08-16 22:48:45

+0

@carlmeyer谢谢!更新为可重用函数 – Integralist 2016-08-17 19:59:21

+0

正如我在Evan的回答中提出的一个评论,另一种可能性也会处理递归函数:eval“$(declare -f theirfunc | sed's/\ btheirfunc \ b/orig_theirfunc/g) “ – 2016-11-25 11:59:56

2

这是基于@Evan布罗德的做法功能:

# Syntax: rename_function <old_name> <new_name> 
function rename_function() 
{ 
    local old_name=$1 
    local new_name=$2 
    eval "$(echo "${new_name}()"; declare -f ${old_name} | tail -n +2)" 
    unset -f ${old_name} 
} 

一旦被定义,你可以简单地做rename_function func orig_func

请注意,您可以使用相关方法来修饰/修改/包装现有功能,如@ phs的回答:

# Syntax: prepend_to_function <name> [statements...] 
function prepend_to_function() 
{ 
    local name=$1 
    shift 
    local body="[email protected]" 
    eval "$(echo "${name}(){"; echo ${body}; declare -f ${name} | tail -n +3)" 
} 

# Syntax: append_to_function <name> [statements...] 
function append_to_function() 
{ 
    local name=$1 
    shift 
    local body="[email protected]" 
    eval "$(declare -f ${name} | head -n -1; echo ${body}; echo '}')" 
} 

一旦这些被定义,假设你有一个现有的功能如下:

function foo() 
{ 
    echo stuff 
} 

然后,你可以这样做:

prepend_to_function foo echo before 
append_to_function foo echo after 

使用declare -f foo,我们可以看到效果:

foo() 
{ 
    echo before; 
    echo stuff; 
    echo after 
} 
5

copy_function可以通过使用shell参数e xpansion代替tail命令:

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

而且golfed的copy_functionrename_function功能:

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

rename_function() { 
    copy_function [email protected] || return 
    unset -f $1 
} 

从@Dmitri鲁宾斯坦的解决方案开始:

  • 不需要调用declare两次。错误检查仍然有效。
  • 通过使用_特殊变量消除温度变化(func)。
    • 注意:使用test -n ...是我能想到的唯一方法来保存_,并仍然能够返回错误。
  • 变化return 1return(它返回当前状态代码)
  • 使用模式替换而不是前缀移除。

一旦copy_function被定义,它使rename_function琐碎。(不要重命名copy_function ;-)

3

如果你只是想预先考虑到的东西的名字,说orig_,那么我认为最简单的是

eval orig_"$(declare -f theirfun)" 
3

总结了所有的其他解决方案和部分纠正他们,这里是一个解决方案:

  • 不使用declare两次
  • 不需要外部程序(如tail
  • 确实没有意外替换
  • 较短
  • 保护您免受通常的编程错误感谢纠正报价

但是:

  • 它可能不会在递归函数的工作,因为复制中用于递归的函数名称不会被替换。获得这样的替代权是一项非常复杂的任务。如果你想使用这样的替换,你可以试试这个回答https://stackoverflow.com/a/18839557 whith eval "${_//$1/$2}"而不是eval "${_/$1/$2}"(注意双//)。然而更换名字太简单函数名失败(如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"; 
} 

现在测试:

somefn() { echo one; } 
rename_fn somefn thatfn 
somefn() { echo two; } 
somefn 
thatfn 

按要求输出:

two 
one 

现在尝试一些更复杂的情况,这都产生预期的结果或失败:

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 

人们可以争辩说,下面仍然是一个错误:

a(){ echo HW; }; rename_fn a " b "; echo $?; b 

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

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

现在,这也捕捉到了这个假象。 (请注意:printf%qbash内置)

当然你也可以拆分此成复制+改名这样的:

copy_fn() { local a; a="$(declare -f "$1")" && eval "function $(printf %q "$2") ${a#*"()"}"; } 
rename_fn() { copy_fn "[email protected]" && unset -f "$1"; } 

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

0

对于我们这些被迫兼容bash 3.2(你知道我们在说谁),declare -f不起作用。我发现type可以工作

eval "$(type my_func | sed $'1d;2c\\\nmy_func_copy()\n')" 

在函数形式,它看起来像

copy_function() 
{ 
    eval "$(type "${1}"| sed $'1d;2c\\\n'"${2}"$'()\n')" 
} 

如果你真的想不依靠sed ...

function copy_function() 
{ 
    eval "$({ 
    IFS='' read -r line 
    IFS='' read -r line 
    echo "${2}()" 
    while IFS='' read -r line || [[ -n "$line" ]]; do 
    echo "$line" 
    done 
    }< <(type "${1}"))" 
} 

但是,这是一个有点罗嗦对我来说