
(這個問題使用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