コマンドのみを認識するユーティリティを使用して `zsh` 関数を処理する

コマンドのみを認識するユーティリティを使用して `zsh` 関数を処理する

(この質問では Maccaffeinateツールを例として使用していますが、この概念は、xargs引数としてユーティリティを受け入れるすべてのツール (つまり ) に適用されます。)

Mac のcaffeinateツールは、ユーティリティの名前 ( ) を受け入れますcaffeinate sleep 1(ここでsleepはユーティリティ)。zshツール自体を変更せずに、関数も受け入れる方法はありますか? たとえば、次のようになります。

function mysleep {
  sleep 2
}

caffeinate mysleep    # mysleep: No such file or directory

編集: この質問は確かに Bash の重複です。回答を教えてくれてありがとう。ただし、zshexport -fでは機能しません。zsh でこれを行う方法はありますか? (混乱を減らすためにタグを削除していますbash。)

答え1

caffeinate新しいプロセスでコマンドを実行することを期待します。

関数を解釈するにはzshzshコマンドが必要です。

そして、その関数の定義(および必要になる可能性のある他の関数)を渡す必要があります。たとえば、次のようになります。

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

functions mysleep関数を呼び出す前に解釈のためにmysleepnew に渡す関数の定義をダンプします。そのため、呼び出された関数は次のように解釈することになります。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])

後者のアプローチにはいくつかの利点があると思います。

  • 完全な制御権があります。関数定義をどのように渡し、どのように使用するかがわかっています。ここでの shellshock のような厄介な事態が発生する余地は少なくなります。
  • 関数定義を保持する bash 環境変数の名前に%文字が含まれているため (含まれていなくても、sudoたとえば を考えてみてください)、caffeinateそれが実行されるコマンドに伝播されるかどうかは保証されませんbash
  • 伝播する場合、関数定義はargv[]ではなくenvp[]に格納されるため、その環境で実行される他のすべてのコマンドの環境を汚染することになります(sleepこの例の場合も含みます)。
  • (マイナー) シェル コードは短くなりますがbash、渡されるデータが増えるためexecve()、そのシステム コールの E2BIG 制限に大きく影響します。

環境を使用したい場合は、次のようにすることもできます。

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

ここでの場合、関数の実行中に を実行するだけでcaffeinateよく、必ずしも関数を実行する必要はありません。次のような他のアプローチを使用できます。caffeinate

mysleep | caffeinate cat

cat実行される限り実行されますmysleepmysleepただし、別のプロセスで実行され、標準出力に影響しますmysleep

mysleep 3> {fd}>(caffeinate cat)

両方の問題を解決します。

mysleep上記のように、との間にパイプが作成されますcat。ただし、パイプの書き込み側は、 に格納されている 10 を超える新しく割り当てられたファイル記述子上にあり、$fd通常mysleepは書き込まれません。したがって、 は何も読み取らず、パイプのファイルの終わりまで待機します。これは、 (およびその fd を継承するすべての子プロセス)が終了したcat場合にのみ発生します。mysleep

答え2

これを行う最善の方法は、関数をスクリプト ファイルに配置し、次のようにスクリプトを呼び出すことです。

caffeinate myfunction.sh

myfunction.sh の内容:

#!/bin/bash
sleep 2

スクリプト ファイルが実行可能であることを確認してください。そうでない場合は、権限エラーが発生します。

chmod +x myfunction.sh

関連情報