Bash:将函数作为参数传递


91

我需要在Bash中将函数作为参数传递。例如,以下代码:

function x() {
  echo "Hello world"
}

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

around x

应该输出:

before
Hello world
after

我知道eval在这种情况下是不正确的,但这只是一个例子:)

任何的想法?

Answers:


126

如果您不需要延迟函数名称或其参数的求值之类的东西,则不需要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; "$@"; echo after; }

around x 1st 2nd

版画

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

2
如果我还有另一个y()函数,可以围绕x 1st 2nd y 1st 2nd做吗?它怎么知道x和y是大约参数,而1st和2nd是x和y参数呢?
techguy2000

您可以省略功能词。
jasonleonhard

但是,它们将不会以名称分隔。即,如果您在方法内部有方法并且使用了function单词,则必须运行顶级方法才能访问这些内部方法。
jasonleonhard

29

我认为没有人完全回答这个问题。他没有问是否可以按顺序回显字符串。相反,问题的作者想知道他是否可以模拟函数指针行为。

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

来自作者:

function x() {
  echo "Hello world"
}

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

around x

为了扩展它,我们将使用函数x echo“ Hello world:$ 1”来显示何时真正执行函数。我们将传递一个字符串,该字符串是函数“ x”的名称:

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

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

around x

为了描述这一点,字符串“ x”被传递到函数echo(),该函数回显“ before”,然后调用函数x(通过变量$ 1,第一个传递给around的参数),传递参数“ 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,在这里我们执行了存储在变量Z中并传递了参数10 20和30的名为“ x”的函数。

在上面我们通过为函数分配变量名称来引用函数,以便我们可以使用变量代替实际知道函数名称(这与在c中非常经典的函数指针情况下为概括程序流而可能执行的操作类似, -选择要基于命令行参数进行的函数调用)。

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


这个答案很棒。我制作了所有示例的bash脚本并运行了它们。我也非常喜欢您如何使“仅改变”的制造者受益匪浅。您的倒数第二段有一个错误的拼写:“以上”
JMI MADISON

错字固定。感谢@J MADISON
uDude

7
你为什么要包装它而()不会启动子外壳?
horseyguy18年

17

无需使用 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() {
        "$@"
    }

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

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

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

基本上Bash不能这样使用。


2

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

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

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

在这里,结果被输出到标准输出,并且调用者使用命令替换来捕获变量中的值。然后可以根据需要使用该变量。


1

您应该遵循以下原则:

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

然后你可以打电话 around x


-1

评估可能是实现此目标的唯一方法。唯一真正的缺点是它的安全性,因为您需要确保不会传入任何恶意,并且只会调用您要调用的函数(以及检查它是否没有讨厌的字符,例如';'在其中)。

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


评估是唯一的方法。
韦斯

1
您可以eval $1使用if declare -F "$1" >/dev/null; then eval $1; fi
user123444555621 2011年

2
...甚至更好:eval $(declare -F "$1")
user123444555621 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.