【问题标题】:Is it possible to define a function dynamically in ZSH?是否可以在 ZSH 中动态定义函数?
【发布时间】:2019-10-25 08:19:52
【问题描述】:

我想在 ZSH 中动态定义一系列函数。

例如:

#!/bin/zsh
for action in status start stop restart; do
     $action() {
         systemctl $action $*
     }
done

但是,这会导致四个相同的函数都调用最后一个参数:

$ status libvirtd
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
Authentication is required to restart 'libvirtd.service'.
...

有没有办法像这样动态定义这些函数?

【问题讨论】:

    标签: function metaprogramming zsh function-definition


    【解决方案1】:

    是的,其实很简单:

    for action in status start stop restart
    do
        $action() {
            systemctl $0 "$@"
        }
    done
    

    这里的重点是$0的使用。您原来的解决方案的问题是函数定义中的“$action”在定义期间没有展开,所以在所有四个函数中它只是引用了这个变量的最后一个值。因此,与其尝试使用 eval(如另一个解决方案中所建议的那样)让它与丑陋的诡计一起工作,最好的解决方案是使用 $0... 在 shell 脚本中,$0 扩展为当前脚本的名称,而在 shell函数,它扩展为当前函数的名称。这恰好正是您想要的!

    还要注意我是如何使用"$@"(引号很重要)而不是$*。这适用于带空格的引用参数,$* 会破坏。

    最后,对于这个用例,您可以使用“别名”而不是函数,一切都会简单得多:

    for action in status start stop restart
    do
        alias $action="systemctl $action"
    done
    

    【讨论】:

      【解决方案2】:

      这是可能的,但很难看:

      for action in status start stop restart; do
          eval "$action() { systemctl $action \"\$@\"; }"
      done
      

      与涉及eval 的任何事情一样,这很难做到正确。 eval 所做的事情是将命令解析两次,并在第二次解析时执行它。 “嗯?”我听你说?好吧,问题是通常$variable 函数定义中的引用不会立即扩展,而是在函数执行时。因此,当您的循环运行时(action 设置为“状态”):

      $action() {
          systemctl $action $*
      done
      

      它扩展了对$action 的第一个引用,而不是第二个,给出了这个:

      status() {
          systemctl $action $*
      done
      

      相反,您希望 both$action 的引用立即展开。但是您希望立即扩展对$* 的引用,因为那样它会使用脚本的参数,而不是运行时提供给函数的参数。实际上,您根本不想要$*,因为它在某些情况下会破坏论点;请改用"$@"

      因此,您需要一种方法来立即扩展一些变量/参数引用,并将一些推迟到以后。 eval 给你。最棘手的事情是您可能需要两个级别的引用/转义(一个用于第一次解析传递,一个用于第二次解析),并且您需要使用这些级别来控制哪些变量/参数引用立即展开,哪些稍后展开。

      当它运行时(action 设置为“状态”):

      eval "$action() { systemctl $action \"\$@\"; }"
      

      ...它进行解析传递,扩展未转义的变量引用并删除引用和转义的级别,给出:

      status() { systemctl status "$@"; }
      

      ...这就是你想要的。

      【讨论】:

      • 这个问题实际上有不丑的解决方案 :-) 在下面查看我的答案。
      猜你喜欢
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      • 2011-04-06
      • 2020-08-24
      • 2010-09-25
      • 2015-05-13
      相关资源
      最近更新 更多