使用僅識別命令的 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

相關內容