(这个问题使用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最终解释:zsh
zsh
caffeinate
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
本例中的示例)。 - (次要)尽管
bash
shell 代码更短,但传递的数据更多,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