【问题标题】:Bash: pass a function as parameterBash:将函数作为参数传递
【发布时间】:2011-08-06 01:11:22
【问题描述】:

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

function x() {
  echo "Hello world"
}

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

around x

应该输出:

before
Hello world
after

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

有什么想法吗?

【问题讨论】:

    标签: bash function callback parameter-passing


    【解决方案1】:

    如果你不需要任何花哨的东西,比如延迟函数名或其参数的评估,你不需要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
    

    【讨论】:

    • 如果我有另一个 y() 函数,我可以在 x 1st 2nd y 1st 2nd 左右做吗?它怎么知道 x 和 y 是 around 而 1st 和 2nd 是 x 和 y 的参数?
    • 而且可以省略虚词。
    • 但是,它们不会是命名空间的。即,如果您在方法内部有方法并且保留function 字样,则在运行顶级方法之前您无法访问这些内部方法。
    【解决方案2】:

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

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

    来自作者:

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

    为了扩展它,我们将使用 function 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”被传递给函数around(),它回显“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 中的名为“x”的函数并传递参数 10 20 和 30。

    在上面我们通过为函数分配变量名称来引用函数,这样我们就可以使用变量来代替实际知道函数名称(这类似于您在 c 中用于泛化程序的非常经典的函数指针情况中可能会做的事情流,但预先选择您将根据命令行参数进行的函数调用)。

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

    【讨论】:

    • 这个答案太棒了。我制作了所有示例的 bash 脚本并运行它们。我也非常喜欢你如何让“唯一改变”的制造者受益匪浅。您的倒数第二段拼写错误:“Aabove”
    • 已修复错字。谢谢@J麦迪逊
    • 你为什么把它包裹在() 不会启动一个子shell?
    【解决方案3】:

    没有必要使用eval

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

    【讨论】:

      【解决方案4】:

      除了字符串之外,您不能将任何内容传递给函数。过程替换可能会伪造它。 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 仍然只得到一个恰好是函数名称的字符串(从环境中的序列化自动导入)及其参数。

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

      Bash 基本上不能这样用。

      【讨论】:

        【解决方案5】:

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

        function myfunc()
        {
            local  myresult='some value'
            echo "$myresult"
        }
        
        result=$(myfunc)   # or result=`myfunc`
        echo $result
        

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

        【讨论】:

          【解决方案6】:

          你应该有一些类似的东西:

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

          然后您可以拨打around x

          【讨论】:

            【解决方案7】:

            eval 可能是完成它的唯一方法。唯一真正的缺点是它的安全方面,因为您需要确保没有恶意传入,并且只会调用您想要调用的函数(同时检查它是否没有像“;”这样的讨厌字符也在里面)。

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

            【讨论】:

            • eval 是唯一的方法。
            • 您可以轻松检查eval $1 是否会使用if declare -F "$1" &gt;/dev/null; then eval $1; fi 调用函数
            • ...甚至更好:eval $(declare -F "$1")
            猜你喜欢
            • 2021-10-05
            • 2021-04-13
            • 2023-04-02
            • 2013-01-27
            • 2010-11-06
            相关资源
            最近更新 更多