caffeinate
espera executar uma comando em um novo processo.
Para interpretar uma função zsh
, você precisa de um comando zsh
.
E você precisaria passar a definição dessa função (assim como outras funções que ela pode precisar) para ela, por exemplo com:
mysleep() {
sleep 2
}
caffeinate zsh -c "$(functions mysleep);mysleep"
functions mysleep
despeja a definição da função mysleep
que passamos para o novo zsh
para interpretação antes de chamar a função, para que zsh
invocado por caffeinate
acabe interpretando:
mysleep() {
sleep 2
};mysleep
Se compararmos a bash
:
mysleep() {
sleep 2
}
export -f mysleep
caffeinate bash -c "mysleep"
(que é 2 caracteres mais curto para digitar), bash
fará:
execve("/path/to/caffeinate",
["caffeinate", "bash", "-c", "mysleep"],
["BASH_FUNC_mysleep%%=() { sleep 2\n}", rest-of-environment])
Enquanto com zsh
, obtemos:
execve("/path/to/caffeinate",
["caffeinate", "zsh", "-c", "mysleep () {\n\tsleep 2\n};mysleep"],
[rest-of-environment])
Eu vejo várias vantagens dessa última abordagem:
- temos controle total: sabemos como passamos a definição da função, como ela está sendo usada. Há menos espaço para coisas desagradáveis como o tipo de shellshock aqui.
- como o nome da variável de ambiente bash que transporta a definição de função contém
%
caracteres (e mesmo que não tenha, pense emsudo
por exemplo), não temos garantia de quecaffeinate
será propagá-lo para o comandobash
que ele executa. - se for propagado, porque a definição da função é armazenada em envp [] em vez de argv [], isso significa que polui o ambiente de todos os outros comandos executados nesse ambiente (incluindo
sleep
, por exemplo, neste exemplo). - (menor), embora o código shell
bash
seja menor, mais dados são transmitidos paraexecve()
, de modo que contribui mais para o limite E2BIG dessa chamada de sistema.
Se você quiser usar o ambiente, ainda é possível fazer isso:
FUNCS=$(functions mysleep) caffeinate zsh -c '
eval "$FUNCS";mysleep'
No caso de caffeinate
aqui, é onde precisamos apenas que caffeinate
seja executado enquanto a função está em execução, não necessariamente para executar a função, podemos usar outras abordagens como:
mysleep | caffeinate cat
cat
será executado enquanto mysleep
for executado. mysleep
ainda seria executado em um processo separado e isso afeta o stdout de mysleep
.
mysleep 3> {fd}>(caffeinate cat)
resolveria os dois problemas.
Como acima, isso cria um canal entre mysleep
e cat
. Mas a extremidade de gravação do pipe agora está em um descritor de arquivo alocado recentemente acima de 10 (armazenado em $fd
) para o qual mysleep
normalmente não grava. Portanto, cat
não lerá nada, mas esperará até o final do arquivo no canal, o que só acontecerá quando mysleep
(e todos os processos filhos que herdam esse fd) terminar.