2011-04-15 168 views
49

我需要传递一个函数作为Bash中的参数。例如,下面的代码:Bash:传递函数作为参数

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    eval $1 
    echo "after" 
} 

around x 

应该输出:

before 
Hello world 
after 

我知道eval是不正确的在这方面不过这只是一个例子:)

任何想法?

回答

67

如果你不需要任何幻想像延迟功能名称或参数的评估,你不需要eval

function x()  { echo "Hello world";   } 
function around() { echo before; $1; echo after; } 

around x 

做你想做的。你甚至可以通过功能和它的参数是这样的:

function x()  { echo "x(): Passed $1 and $2"; } 
function around() { echo before; "[email protected]"; echo after; } 

around x 1st 2nd 

打印

before 
x(): Passed 1st and 2nd 
after 
0

eval可能是实现它的唯一方法。唯一真正的缺点是它的安全性方面,因为你需要确保没有恶意传入,只有你想调用的函数会被调用(并检查它没有像';'这样的讨厌字符)也在其中)。

所以,如果你是调用代码的人,那么评估可能是唯一的方法。请注意,还有其他形式的eval可能会涉及子命令($()和``),但它们并不安全并且更昂贵。

+0

eval是唯一的方法。 – Wes 2011-04-15 04:18:05

+1

你可以很容易地检查'eval $ 1'是否会使用'if declare -F“$ 1”>/dev/null来调用一个函数;然后评估$ 1; fi' – user123444555621 2011-04-15 04:26:14

+2

...或甚至更好:'eval $(declare -F“$ 1”)' – user123444555621 2011-04-15 04:58:03

1

你应该沿着线的东西:

function around() 
{ 
    echo 'before'; 
    echo `$1`; 
    echo 'after'; 
} 

你可以调用around x

1

一个更好的方法是在你的函数中使用局部变量。问题就变成了如何让调用者获得结果。一种机制是使用命令替换:

function myfunc() 
{ 
    local myresult='some value' 
    echo "$myresult" 
} 

result=$(myfunc) # or result=`myfunc` 
echo $result 

在这里,结果被输出到标准输出和所述呼叫者使用命令替换捕获在一个变量的值。然后可以根据需要使用该变量。

13

没有必要使用eval

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    var=$($1) 
    echo "after $var" 
} 

around x 
5

你不可错过什么不是字符串的函数。流程替换可以将其伪造。 Bash倾向于保持开放FIFO直到它扩展完成的命令。

这里有一个快速痴心一片

foldl() { 
    echo $(($(</dev/stdin)$2)) 
} < <(tr '\n' "$1" <$3) 

# Sum 20 random ints from 0-999 
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done) 

功能可以出口,但这不是一样有趣它第一次出现。我发现它主要用于使脚本或运行脚本的其他程序可以访问调试功能。

(
    id() { 
     "[email protected]" 
    } 

    export -f id 
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi' 
) 

id仍然只得到恰好是一个函数(从一个序列化环境中自动导入)及其参数的个数名称的字符串。

Pumbaa80对另一个答案的评论也很好(eval $(declare -F "$1")),但它主要用于数组,而不是函数,因为它们总是全局的。如果你要在一个函数中运行它,它所要做的就是重新定义它,所以没有任何效果。它不能用于根据当前范围内发生的任何事情来创建闭包或部分函数或“函数实例”。充其量,这可以用来将函数定义存储到其他地方重新定义的字符串中 - 但这些函数也只能使用硬编码,除非使用eval

基本上Bash不能像这样使用。

9

我不认为有人会回答这个问题。他没有问他是否可以按顺序回音。问题的作者想知道他是否可以模拟函数指针行为。

有几个答案很像我会做的,我想用另一个例子来扩展它。

从笔者:

function x() { 
    echo "Hello world" 
} 

function around() { 
    echo "before" 
    ($1)     <------ Only change 
    echo "after" 
} 

around x 

为了扩大这一点,我们将有函数x回声“世界,你好:$ 1”,显示当函数执行真正发生。我们将通过一个字符串,它的功能是“X”的名称:)传递给函数左右(其中回声报“之前”

function x() { 
    echo "Hello world:$1" 
} 

function around() { 
    echo "before" 
    ($1 HERE)     <------ Only change 
    echo "after" 
} 

around x 

为了说明这一点,字符串“X”,调用函数x (通过变量$ 1,第一个传递给参数的参数)传递参数“HERE”,最后回声。

另一方面,这是使用变量作为函数名称的方法。变量实际上包含作为函数名称的字符串,并且($ variable arg1 arg2 ...)调用传递参数的函数。见下文:

function x(){ 
    echo $3 $1 $2  <== just rearrange the order of passed params 
} 

Z="x"  # or just Z=x 

($Z 10 20 30) 

给出:30 10 20,在那里我们执行命名的函数“X”存储在变量Z和通过分配的变量名传递的参数10 20和30

以上在这里我们参考函数所以我们可以使用变量来代替实际知道函数名称的地方(这类似于你在c中的一个非常经典的函数指针情况下可以做的事情,用于推广程序流但是预先选择你将要进行的函数调用基于命令行参数)。

在bash中,这些不是函数指针,而是指向稍后使用的函数名称的变量。

+0

这个答案很棒。我做了所有例子的bash脚本并且运行它们。我也非常喜欢你如何制造“只有改变”的制造商,这对我们有很大的帮助。 您的倒数第二段有错误拼写:“Aabove” – 2017-09-12 21:28:39

+0

错字固定。谢谢@J MADISON – uDude 2017-09-14 20:18:10

+0

为什么你用'()'包装它不会启动一个子shell? – horseyguy 2018-01-12 02:28:39