web-dev-qa-db-ja.com

zshで独自のシェル関数を定義およびロードする方法

Zshで自分のシェル関数を定義して実行するのに苦労しています。私は公式ドキュメントの 説明 に従い、簡単な例を最初に試しましたが、うまくいきませんでした。

フォルダがあります:

~/.my_zsh_functions

このフォルダーには、rwxユーザー権限を持つfunctions_1というファイルがあります。このファイルでは、次のシェル関数を定義しています。

my_function () {
echo "Hello world";
}

FPATHを定義して、フォルダー~/.my_zsh_functionsへのパスを含めます。

export FPATH=~/.my_zsh_functions:$FPATH

フォルダ.my_zsh_functionsecho $FPATHまたはecho $fpathで関数パスにあることを確認できます

しかし、私がシェルから次のことを試した場合:

> autoload my_function
> my_function

私は得ます:

zsh:my_test_function:関数定義ファイルが見つかりません

my_functionを呼び出すために他に必要なことはありますか?

更新:

これまでの答えは、zsh関数を使用してファイルを調達することを示唆しています。これは理にかなっていますが、私は少し混乱しています。 zshはこれらのファイルがFPATHでどこにあるかを知らないのですか?では、autoloadの目的は何ですか?

60

Zshでは、関数検索パス($ fpath)は一連のディレクトリを定義します。ディレクトリには、それらに含まれる関数が初めて必要になったときに自動的にロードされるようにマークできるファイルが含まれます。

Zshには、ファイルを自動ロードする2つのモードがあります。Zshのネイティブな方法と、kshの自動ロードに似た別のモードです。後者は、KSH_AUTOLOADオプションが設定されている場合にアクティブになります。 Zshのネイティブモードがデフォルトであり、ここでは他の方法については説明しません(kshスタイルの自動ロードの詳細については、「man zshmisc」および「man zshoptions」を参照してください)。

はい。ディレクトリ `〜/ .zfunc 'があり、それを関数検索パスの一部にしたい場合は、次のようにします。

_fpath=( ~/.zfunc "${fpath[@]}" )
_

これにより、プライベートディレクトリが検索パスのfrontに追加されます。これは、zshのインストールからの関数を独自のものでオーバーライドする場合に重要です(たとえば、zshのCVSリポジトリの `_git 'などの更新された補完関数を古いバージョンのシェルでインストールして使用する場合など)。

`$ fpath 'からのディレクトリは再帰的に検索されないことも注目に値します。プライベートディレクトリを再帰的に検索する場合は、次のように自分で処理する必要があります(次のスニペットでは、 `EXTENDED_GLOB 'オプションを設定する必要があります)。

_fpath=(
    ~/.zfuncs
    ~/.zfuncs/**/*~*/(CVS)#(/N)
    "${fpath[@]}"
)
_

それは訓練されていない目には不可解に見えるかもしれませんが、実際には「〜/ .zfunc」の下のすべてのディレクトリを「$ fpath」に追加するだけで、「CVS」と呼ばれるディレクトリを無視します(これは、全体をチェックアウトする場合に便利です。 zshのCVSからプライベート検索パスへの関数ツリー)。

次の行を含むファイル `〜/ .zfunc/hello 'があるとしましょう:

_printf 'Hello world.\n'
_

ここで行う必要があるのは、mark最初の参照時に自動的にロードされる関数です。

_autoload -Uz hello
_

「-Uzとは何ですか?」と尋ねますか?さて、それはオプションのセットにすぎず、他にどのオプションが設定されていても、 `autoload 'が正しいことを行うようにします。 「U」は関数のロード中にエイリアス展開を無効にし、「z」は何らかの理由で「KSH_AUTOLOAD」が設定されていてもzshスタイルの自動ロードを強制します。

それが終わったら、新しい「hello」関数を使用できます。

zsh%hello 
 Hello world。

これらのファイルの調達についての言葉:それはちょうどwrongです。 `〜/ .zfunc/hello 'ファイルをソースとすると、単に" Hello world "と表示されます。一度。これ以上何もない。関数は定義されません。また、requiredの場合にのみ関数のコードをロードするという考え方です。 「オートロード」呼び出しの後、関数の定義は読み取られません。関数は、必要に応じて後でオートロードされるようにマークされています。

そして最後に、$ FPATHと$ fpathに関するメモ:Zshはそれらをリンクされたパラメーターとして維持します。小文字のパラメーターは配列です。大文字バージョンは文字列スカラーで、エントリ間にコロンで結合されたリンクされた配列のエントリが含まれます。これは、スカラーリストを処理する方が配列を使用する方が自然なためであり、スカラーパラメータを使用するコードの下位互換性も維持します。 $ FPATH(スカラー)を使用する場合は、注意する必要があります。

_FPATH=~/.zfunc:$FPATH
_

次は動作しませんが、動作します:

_FPATH="~/.zfunc:$FPATH"
_

その理由は、チルド展開が二重引用符内で実行されないためです。これが問題の原因である可能性があります。 _echo $FPATH_がチルダを出力し、展開されたパスを出力しない場合、機能しません。安全のために、次のようなチルダの代わりに$ HOMEを使用します。

_FPATH="$HOME/.zfunc:$FPATH"
_

そうは言っても、この説明の冒頭で行ったように、配列パラメーターを使用するほうがはるかに便利です。

また、$ FPATHパラメータをエクスポートしないでください。現在のシェルプロセスでのみ必要であり、その子プロセスでは必要ありません。

更新

`$ fpath 'のファイルの内容について:

Zshスタイルのオートローディングでは、ファイルの内容はそれが定義する関数の本体です。したがって、_echo "Hello world."_という行を含む「hello」という名前のファイルは、「hello」という関数を完全に定義しています。コードの周りにhello () { ... }を自由に配置できますが、これは不必要です。

ただし、1つのファイルには1つの関数しか含まれないという主張は完全に正しいわけではありません。

特に、関数ベースの補完システム(compsys)からいくつかの関数を見ると、それが誤解であることがすぐにわかります。関数ファイルで追加の関数を自由に定義できます。また、関数を初めて呼び出すときに必要になる可能性がある、あらゆる種類の初期化を自由に行うこともできます。ただし、その場合は常にファイル内のファイルのように名前が付けられた関数を定義し、ファイルの最後でその関数を呼び出します。関数が初めて参照されたときに実行されます。

-サブ関数を使用して-ファイル内でファイルのような名前の関数を定義しなかった場合、その関数には関数定義(つまり、ファイル内のサブ関数の関数定義)が含まれることになります。ファイルのような名前の関数を呼び出すたびに、すべてのサブ関数を効果的に定義することになります。通常、これは必要なではないので、ファイル内のファイルのように名前が付けられた関数を再定義します。

短いスケルトンを含めます。これにより、それがどのように機能するかがわかります。

_# Let's again assume that these are the contents of a file called "hello".

# You may run arbitrary code in here, that will run the first time the
# function is referenced. Commonly, that is initialisation code. For example
# the `_tmux' completion function does exactly that.
echo initialising...

# You may also define additional functions in here. Note, that these
# functions are visible in global scope, so it is paramount to take
# care when you're naming these so you do not shadow existing commands or
# redefine existing functions.
hello_helper_one () {
    printf 'Hello'
}

hello_helper_two () {
    printf 'world.'
}

# Now you should redefine the "hello" function (which currently contains
# all the code from the file) to something that covers its actual
# functionality. After that, the two helper functions along with the core
# function will be defined and visible in global scope.
hello () {
    printf '%s %s\n' "$(hello_helper_one)" "$(hello_helper_two)"
}

# Finally run the redefined function with the same arguments as the current
# run. If this is left out, the functionality implemented by the newly
# defined "hello" function is not executed upon its first call. So:
hello "$@"
_

この愚かな例を実行すると、最初の実行は次のようになります。

zsh%hello 
 initializing ... 
 Hello world。

そして、連続した呼び出しは次のようになります。

zsh%hello 
 Hello World。

これで問題が解決することを願っています。

(これらすべてのトリックを使用するより複雑な実世界の例の1つは、zshの関数ベースの補完システムからのすでに言及されている ` _ tmux '関数です。)

103
Frank Terbeck

fpath要素によって名前が付けられたディレクトリ内のファイルの名前は、それが定義するオートロード可能な関数の名前と一致する必要があります。

関数の名前はmy_functionで、~/.my_zsh_functionsfpath内の目的のディレクトリであるため、my_functionの定義はファイル~/.my_zsh_functions/my_functionにある必要があります。

提案されたファイル名の複数形(functions_1)は、ファイルに複数の関数を配置することを計画していたことを示します。これはfpathとオートローディングの仕組みではありません。ファイルごとに1つの関数定義が必要です。

6
Chris Johnsen

source ~/.my_zsh_functions/functions1ターミナルと評価my_function、これで関数を呼び出すことができます

2
harish.venkat

次のように、$ ZDOTDIR/.zshrcにすべての関数を含むファイルを「ロード」できます。

source $ZDOTDIR/functions_file

または、ドット「。」を使用できます。 「ソース」の代わりに。

1
ramonovski

あなたが望んでいるように見えるのは遅延初期化された関数を持つことなので、ソーシングは間違いなく正しいアプローチではありません。それがautoloadの目的です。これがあなたが求めていることを達成する方法です。

~/.my_zsh_functionsに、「hello world」をエコーするmy_functionという関数を置きたいと言います。ただし、これを関数呼び出しでラップします。これは、これが機能する方法ではありません。代わりに、~/.my_zsh_functions/my_functionというファイルを作成する必要があります。その中には、関数ラッパーではなくecho "Hello world"を入れてください。ラッパーが本当に必要な場合は、このようなこともできます。

# ~/.my_zsh_functions/my_function
__my_function () {
    echo "Hello world";
}
# you have to call __my_function
# if this is how you choose to do it
__my_function

次に、.zshrcファイルに以下を追加します。

fpath=(~/.my_zsh_functions $fpath);
autoload -U ~/.my_zsh_functions/my_function

新しいZSHシェルを読み込むときは、which my_functionと入力します。あなたはこれを見るはずです:

my_function () {
    # undefined
    builtin autoload -XU
}

ZSHがmy_functionをautoload -Xで作成しました。次に、my_functionを実行しますが、my_functionと入力するだけです。 Hello worldの出力が表示され、which my_functionを実行すると、次のように関数が入力されているはずです。

my_function () {
    echo "Hello world"
}

autoloadで動作するように~/.my_zsh_functionsフォルダ全体を設定すると、本当の魔法が生まれます。このフォルダにドロップするすべてのファイルをこのように機能させるには、.zshrcに入れるファイルを次のように変更します。

# add ~/.my_zsh_functions to fpath, and then lazy autoload
# every file in there as a function
fpath=(~/.my_zsh_functions $fpath);
autoload -U $fpath[1]/*(.:t)
0
mattmc3