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


54

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

フォルダーがあります:

~/.my_zsh_functions

このフォルダには、私はと呼ばれるファイル持っているfunctions_1rwxユーザー権限を。このファイルには、次のシェル関数が定義されています。

my_function () {
echo "Hello world";
}

FPATHフォルダーへのパスを含めるように定義しました~/.my_zsh_functions

export FPATH=~/.my_zsh_functions:$FPATH

または.my_zsh_functionsでフォルダが機能パスにあることを確認できますecho $FPATHecho $fpath

ただし、シェルから次のことを試してみると:

> autoload my_function
> my_function

私は得る:

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

電話をかけるために他に必要なことはありますmy_functionか?

更新:

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


$ ZDOTDIRが適切に定義されていることを確認してください。zsh.sourceforge.net/Intro/intro_3.html
ramonovski

2
$ ZDOTDIRの値は、この問題とは関係ありません。この変数は、zshがユーザーの構成ファイルを検索する場所を定義します。設定されていない場合は、代わりに$ HOMEが使用されます。これはほとんどすべての人にとって正しい値です。
フランクターベック

回答:


95

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

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

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

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

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

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

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

訓練を受けていない人にはわかりにくいかもしれませんが、実際には「〜/ .zfunc」以下のすべてのディレクトリを「$ fpath」に追加しますが、「CVS」と呼ばれるディレクトリを無視しますzshのCVSからプライベート検索パスへの関数ツリー)。

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

printf 'Hello world.\n'

ここで必要なのは、最初の参照時に関数が自動的にロードされるようにマークすることです。

autoload -Uz hello

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

面倒を見てから、新しい「hello」関数を使用できます。

zsh%こんにちは
こんにちは世界。

これらのファイルの調達について一言:それは間違っています。その `〜/ .zfunc / hello 'ファイルを入手した場合、「Hello world」と表示されます。一度。これ以上何もない。関数は定義されません。また、必要なときにのみ関数のコードをロードするという考え方もあります。「autoload」呼び出しの後、関数の定義は読み込まれません。関数は、必要に応じて後で自動ロードされるようにマークされています。

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

FPATH=~/.zfunc:$FPATH

動作しますが、以下は動作しません:

FPATH="~/.zfunc:$FPATH"

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

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

そうは言っても、この説明の冒頭で行ったように、配列パラメーターを使用したいです。

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

更新

`$ fpath 'のファイルの内容に関して:

zshスタイルのオートロードでは、ファイルのコンテンツは定義する関数の本体です。したがって、行を含む「hello」という名前のファイルは、「hello」echo "Hello world."という関数を完全に定義します。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%こんにちは
初期化しています...
こんにちは世界。

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

zsh%こんにちは
こんにちは世界。

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

(これらのすべてのトリックを使用するより複雑な実世界の例の1つは、zshの関数ベースの補完システムですでに説明した ` _tmux '関数です。)


ありがとうフランク!他の回答では、ファイルごとに1つの関数しか定義できないと読んでいますが、そうですか?my_function () { }あなたのHello world例では構文を使用していないことに気付きました。構文が不要な場合、いつ使用すると便利ですか?
アメリオバスケスレイナ

1
これらの質問にも対処するために、元の回答を拡張しました。
フランクターベック

「常にファイル内のファイルのような名前の関数で定義し、ファイルの最後でその関数を呼び出します」:なぜですか?
Hibou57 14

Hibou57:(それは誤字です。「関数を定義する」必要がありますが、現在修正されています。)以下のコードスニペットを考慮すると、それは明確だと思いました。とにかく、もう少し文字通りの理由を説明する段落を追加しました。
フランクテルベック14

こちらがあなたのサイトの詳細です。ありがとうございます。
ティモ

5

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

あなたの関数は名前が付けられmy_function、あなた~/.my_zsh_functionsの中で意図されたディレクトリなfpathので、の定義はmy_functionファイルにあるべきです~/.my_zsh_functions/my_function

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


2

与えsource ~/.my_zsh_functions/functions1ターミナルで、評価my_function、今、あなたは、関数を呼び出すことができるようになります


2
おかげで、しかし何の役割があるFPATHautoload、その後?なぜファイルも入手する必要があるのですか?更新された質問をご覧ください。
アメリオバスケスレイナ

1

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

source $ZDOTDIR/functions_file

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


1
おかげで、しかし何の役割があるFPATHautoload、その後?なぜファイルも入手する必要があるのですか?更新された質問をご覧ください。
アメリオバスケスレイナ

0

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

~/.my_zsh_functionsmy_function「hello world」をエコーするという関数を置きたいと言います。ただし、これを関数呼び出しでラップします。これは、これが機能する方法ではありません。代わりに、というファイルを作成する必要があります~/.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_functionHello world印刷が表示され、実行するとwhich my_function次のように関数が入力されていることがわかります。

my_function () {
    echo "Hello world"
}

~/.my_zsh_functionsフォルダー全体をで動作するように設定すると、本当の魔法が生まれますautoload。このフォルダにドロップするすべてのファイルがこのように動作するようにしたい場合は、何を入れるかを次のように変更します.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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.