bash上的嵌套函数

问题描述 投票:3回答:3

我有一个函数,期望接收另一个要执行的函数

  a(){
     function=$1
     echo "common log! $function"
     $function --> run the function
  }

我想要的是将我的函数中的函数参数作为嵌套函数传递

   b(){
       a f(){ echo "nested function b" }
       echo "since I´m doing more things here"
   }

   c(){
       a f(){ echo "nested function c" }
       echo "since I´m doing more things here"
   }

但似乎嵌套函数f无法在bash上完成

关于如何实现这一点的任何建议?

bash function
3个回答
3
投票

您可以使用子shell函数来嵌套函数 - 使用括号而不是大括号:

#!/bin/bash

a() {
    echo "hello a"
}

# Note the (
b() (
    echo "hello b"
    a() {
        echo "inner a"
    }

    a
)

a
b
a

得到:

hello a
hello b
inner a
hello a

你不能做的是将内部函数传递到别处,因为内部函数只存在于子shell中。 Bash没有对函数的引用,只有全局名称。

如果你想编写类似闭包的代码,那么使用像Python这样的语言。


2
投票

只需定义并按名称传递:

b(){
    f(){ echo "nested function"; }
    a f
    echo "since I´m doing more things here"
}

但请注意,nested functions don't seem to be a thing in bash。 因此,在运行上述代码后,f将在全球范围内提供。 因此上面的代码相当于:

f(){
    echo "nested function"
}
b(){
    a f
    echo "since I´m doing more things here"
}

1
投票

正如其他回答者所说,Bash只支持全局功能。但是,这不需要阻止你。所有你需要做的就是将你的“内部”功能定义为全局功能,但只要给它一个名字,别人就不会使用它。这是一种称为“去功能化”的技术。请注意,您必须非常小心,以避免执行恶意代码,或者它可能像小提琴一样开始播放您的脚本。

__inner_b_key=0 # one counter per script, or function, or inner function, or whatever
b() {
    local f
    f=__inner_b_f_$((inner_b_key++))
    # Yes, it's evil
    # But, it's powerful
    eval "function ${f}() { echo \"nested function b\"; }"
    a $f
}

你可以用另一个技巧“lambda lifting”来构建它。如果你的内在功能有任何复杂性,eval强制要求的引用会很快让你疯狂。 Lambda提升是将内部函数提升为全局函数,将其自由变量转换为参数的过程。由于Bash已经将“本地”功能提升到全局范围,因此您可以获得非常好的效果:

__inner_c_key=0
c() {
   local f
   local computed
   computed=$(doSomething)
   # Do something with computed
   __inner_c_f() {
       # local variables are received as the first arguments
       local computed
       computed=$1
       shift
       # the arguments passed to the wrapper appear after the closure arguments
       # Do another thing with computed
   }
   # a closure is created, by storing the values of c's local variables into
   # the global definition of an anonymous function that wraps the real implementation
   # said anonymous wrapper also forwards the arguments it receives after the arguments
   # consumed to pass the closure
   # ${var@Q} is a Bash 4.4 feature that quotes the value $var such that Bash can
   # reinterpret it back to the same value, which is perfect for eval
   f=__inner_c_f_$((inner_c_key++))
   eval "function ${f}() { __inner_c_f ${computed@Q} \"\$@\"; }"
   higherOrder $f
   # a function with a closure was passed to a higher order function! in Bash!
   # even after this call to c ends, the value of computed lives on inside
   # the definition of $f, which is global and acts as the closure
   # too bad we lack garbage collection!
}

这些转换非常机械化,以至于你似乎充当编译器,通过利用与其他编译器相同的技术将功能代码转换为命令式代码,将假设的“功能性Bash”转换为普通的Bash。当你遵循这个机械过程时,你可以很容易地确保你的evals是理智而不是邪恶的。

© www.soinside.com 2019 - 2024. All rights reserved.