使用仅识别命令的 util 处理“zsh”函数

使用仅识别命令的 util 处理“zsh”函数

(这个问题使用Maccaffeinate工具作为例子,但这个概念适用于所有xargs接受实用程序作为参数的工具(即)。)

Mac 的caffeinate工具接受实用程序的名称:caffeinate sleep 1例如(其中sleep是实用程序)。有没有办法让它zsh在不修改工具本身的情况下也接受一个函数?例如:

function mysleep {
  sleep 2
}

caffeinate mysleep    # mysleep: No such file or directory

编辑:这个问题确实与 Bash 重复 - 感谢您向我指出答案。然而,对于 zsh 来说,export -f不起作用。有没有办法在 zsh 中做到这一点? (我正在删除bash标签以减少混乱。)

答案1

caffeinate期望在新进程中执行命令。

要解释zsh函数,您需要zsh命令。

您需要将该函数的定义(以及它可能需要的其他函数)传递给它,例如:

mysleep() {
  sleep 2
}
caffeinate zsh -c "$(functions mysleep);mysleep"

functions mysleep在调用函数之前转储mysleep我们传递给 new进行解释的函数定义,以便调用 by最终解释:zshzshcaffeinate

mysleep() {
  sleep 2
};mysleep

如果我们与 进行比较bash

mysleep() {
  sleep 2
}
export -f mysleep
caffeinate bash -c "mysleep"

(输入时要短 2 个字符),bash将执行以下操作:

execve("/path/to/caffeinate",
  ["caffeinate", "bash", "-c", "mysleep"],
  ["BASH_FUNC_mysleep%%=() {  sleep 2\n}", rest-of-environment])

当 时zsh,我们得到:

execve("/path/to/caffeinate",
  ["caffeinate", "zsh", "-c", "mysleep () {\n\tsleep 2\n};mysleep"],
  [rest-of-environment])

我看到后一种方法的几个优点:

  • 我们拥有完全的控制权:我们知道如何传递函数定义,如何使用它。这里发生像炮弹休克这样的令人讨厌的事情的范围较小。
  • 由于携带函数定义的 bash 环境变量的名称包含%字符(即使不包含字符,例如sudo),我们也不能保证caffeinate将其传播到bash它运行的命令。
  • 如果它是传播的,因为函数定义存储在 envp[] 而不是 argv[] 中,这意味着它会污染在该环境中执行的每个其他命令的环境(包括sleep本例中的示例)。
  • (次要)尽管bashshell 代码更短,但传递的数据更多,execve()因此对该系统调用的 E2BIG 限制贡献更大。

如果你想使用该环境,你仍然可以这样做:

FUNCS=$(functions mysleep) caffeinate zsh -c '
  eval "$FUNCS";mysleep'

在这种情况下caffeinate,我们只需要在caffeinate函数运行时运行,而不一定要运行该函数,我们可以使用其他方法,例如:

mysleep | caffeinate cat

cat只要mysleep运行就会运行。mysleep仍然会在单独的进程中运行,但这会影响标准输出mysleep

mysleep 3> {fd}>(caffeinate cat)

将解决这两个问题。

如上所述,这在mysleep和之间创建了一个管道cat。但管道的写入端现在位于 10 以上的新分配文件描述符上(存储在 中$fd),mysleep通常不会写入。cat因此,将不读取任何内容,而是等到管道上的文件末尾,这只有在mysleep(以及继承该 fd 的所有子进程)终止时才会发生。

答案2

执行此操作的最佳方法是将函数放入脚本文件中并按如下方式调用脚本:

caffeinate myfunction.sh

myfunction.sh 的内容:

#!/bin/bash
sleep 2

确保脚本文件是可执行的,否则会出现权限错误:

chmod +x myfunction.sh

相关内容