it-swarm-eu.dev

Jak definovat a načíst svou vlastní funkci Shell v zsh

Mám těžké definovat a provozovat své vlastní funkce Shell v zsh. Sledoval jsem pokyny v oficiální dokumentaci a nejprve jsem vyzkoušel snadný příklad, ale nepodařilo se mi to fungovat.

Mám složku:

~/.my_zsh_functions

V této složce mám soubor s názvem functions_1 s uživatelskými oprávněními rwx. V tomto souboru mám definovanou následující funkci Shell:

my_function () {
echo "Hello world";
}

Definoval jsem FPATH tak, aby obsahoval cestu ke složce ~/.my_zsh_functions:

export FPATH=~/.my_zsh_functions:$FPATH

Mohu potvrdit, že složka .my_zsh_functions je v cestě funkcí s echo $FPATH nebo echo $fpath

Pokud však zkusím z prostředí Shell následující:

> autoload my_function
> my_function

Dostanu:

zsh: my_test_function: soubor s definicí funkce nebyl nalezen

Musím ještě něco udělat, abych mohl zavolat my_function?

Aktualizace:

Odpovědi doposud naznačují, že soubor bude získán pomocí funkcí zsh. To dává smysl, ale jsem trochu zmatená. Neměl by zsh vědět, kde jsou tyto soubory s FPATH? Jaký je tedy účel autoload?

60

V zsh cesta hledání funkcí ($ fpath) definuje sadu adresářů, které obsahují soubory, které mohou být označeny tak, aby se načtly automaticky, když je funkce, kterou obsahují, poprvé potřebná.

Zsh má dva režimy automatického vkládání souborů: Zshův nativní způsob a další režim, který se podobá automatickému vkládání ksh. Ten je aktivní, pokud je nastavena možnost KSH_AUTOLOAD. Výchozí režim Zsh je výchozí a nebudu zde diskutovat o jiném způsobu (podrobnosti o autoloadování ve stylu ksh viz „man zshmisc“ a „man zshoptions“).

Dobře. Řekněme, že máte adresář `~/.zfunc 'a chcete, aby byl součástí cesty hledání funkcí, udělejte toto:

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

Tím se přidá váš soukromý adresář na přední stranu vyhledávací cesty. To je důležité, pokud chcete přepsat funkce z instalace zsh vlastními (například když chcete použít aktualizovanou dokončovací funkci, jako je `_git 'z CVS úložiště zsh se starší nainstalovanou verzí Shell).

Je také třeba poznamenat, že adresáře z `$ fpath 'nejsou prohledávány rekurzivně. Pokud chcete, aby byl váš soukromý adresář prohledáván rekurzivně, budete se o to muset starat sami (takto následující úryvek vyžaduje nastavení možnosti „EXTENDED_GLOB“):

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

Může to vypadat krypticky pro netrénované oko, ale ve skutečnosti pouze přidá všechny adresáře pod `~/.zfunc 'do` $ fpath', zatímco ignoruje adresáře nazvané "CVS" (což je užitečné, pokud plánujete pokladnu celého strom funkcí z CVS zsh do vaší soukromé vyhledávací cesty).

Předpokládejme, že máte soubor `~/.zfunc/hello ', který obsahuje následující řádek:

printf 'Hello world.\n'

Nyní musíte pouze označit funkci, která se automaticky načte při prvním odkazu:

autoload -Uz hello

„O čem je - Uz?“, Ptáte se? No, to je jen sada možností, která způsobí, že 'autoload' udělá správnou věc, bez ohledu na to, jaké možnosti jsou nastaveny jinak. 'U' zakáže rozšíření alias během načítání funkce a `z 'vynucuje automatické načtení zsh stylu, i když je` KSH_AUTOLOAD' nastaven z jakéhokoli důvodu.

Poté, co se o to postaráte, můžete použít novou funkci „ahoj“:

zsh% ahoj 
 Ahoj svět.

Slovo o zdroji těchto souborů: To je prostě špatné . Pokud byste tento soubor `~/.zfunc/hello 'použili jako zdroj, vytisklo by to jen" Ahoj svět ". jednou. Nic víc. Nebude definována žádná funkce. Kromě toho je myšlenkou načíst kód funkce pouze tehdy, když je vyžadován . Po volání „autoload“ není definice funkce přečtena . Funkce je právě označena, aby byla později automaticky podle potřeby automaticky načtena.

A konečně, poznámka o $ FPATH a $ fpath: Zsh je udržuje jako propojené parametry. Parametr malých písmen je pole. Verze s velkými písmeny je skalární skalár, který obsahuje položky z propojeného pole spojené dvojtečkami mezi položkami. To se děje, protože manipulace se seznamem skalárů je mnohem přirozenější pomocí polí, a také při zachování zpětné kompatibility pro kód, který používá skalární parametr. Pokud se rozhodnete použít $ FPATH (skalární), musíte být opatrní:

FPATH=~/.zfunc:$FPATH

bude fungovat, zatímco následující nebude:

FPATH="~/.zfunc:$FPATH"

Důvod je ten, že expanze vln není prováděna v uvozovkách. Toto je pravděpodobně zdroj vašich problémů. Pokud echo $FPATH Vytiskne vlnovku a ne rozšířenou cestu, nebude to fungovat. Abych byl v bezpečí, používal bych $ HOME namísto vlnovky jako je tato:

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

Jak už bylo řečeno, mnohem raději bych použil parametr pole, jako jsem to udělal na začátku tohoto vysvětlení.

Neměli byste také exportovat parametr $ FPATH. Je to potřeba pouze při současném procesu Shell a ne u žádných jeho dětí.

Aktualizace

Pokud jde o obsah souborů v `$ fpath ':

Při automatickém vkládání ve stylu zsh je obsah souboru tělem funkce, kterou definuje. Soubor s názvem „ahoj“ obsahující řádek echo "Hello world." Tedy zcela definuje funkci nazvanou „ahoj“. Můžete volně vkládat kód hello () { ... }, ale to by bylo zbytečné.

Tvrzení, že jeden soubor může obsahovat pouze jednu funkci, však není zcela správné.

Zejména pokud se podíváte na některé funkce z funkčního kompletačního systému (compsys), rychle si uvědomíte, že je to mylná představa. Můžete volně definovat další funkce v souboru funkcí. Můžete také provádět jakoukoli inicializaci, kterou budete možná muset provést při prvním vyvolání funkce. Když však uděláte, budete vždy definovat funkci, která je pojmenována jako soubor v souboru a tuto funkci zavoláte na konci souboru, takže spustí se při prvním odkazu na funkci.

Pokud - s podfunkcemi - nedefinujete funkci pojmenovanou jako soubor v souboru, skončíte tím, že tato funkce bude mít v ní definice funkcí (konkrétně definice podfunkcí v souboru). Efektivně byste definovali všechny své dílčí funkce pokaždé, když zavoláte funkci, která je pojmenována jako soubor. Normálně to není to, co chcete, takže byste znovu definovali funkci, která se jmenuje jako soubor v souboru.

Přidám krátkou kostru, která vám dá představu o tom, jak to funguje:

# 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 "[email protected]"

Pokud byste spustili tento hloupý příklad, první běh by vypadal takto:

zsh% ahoj 
 inicializuje ... 
 Ahoj svět.

A následná volání budou vypadat takto:

zsh% ahoj 
 Ahoj svět.

Doufám, že to vyjasní věci.

(Jedním ze složitějších příkladů reálného světa, který používá všechny tyto triky, je již zmíněná funkce --- _ tmux 'z dokončovacího systému založeného na funkci zsh.)

103
Frank Terbeck

Název souboru v adresáři pojmenovaném elementem fpath se musí shodovat s názvem automaticky definovatelné funkce.

Vaše funkce se jmenuje my_function a ~/.my_zsh_functions je zamýšlený adresář ve vašem fpath, takže definice my_function by mělo být v souboru ~/.my_zsh_functions/my_function.

Množné číslo v navrženém názvu souboru (functions_1) znamená, že jste plánovali vložit do souboru více funkcí. Takto to nefunguje fpath a autoloading. Pro každý soubor byste měli mít jednu definici funkce.

6
Chris Johnsen

dát source ~/.my_zsh_functions/functions1 v terminálu a vyhodnocení my_function, nyní budete moci funkci vyvolat

2
harish.venkat

Můžete "načíst" soubor se všemi funkcemi v $ ZDOTDIR/.zshrc, jako je tento:

source $ZDOTDIR/functions_file

Nebo můžete použít tečku „.“ místo „zdroje“.

1
ramonovski

Sourcing rozhodně není správný přístup, protože to, co zřejmě chcete, je mít líné inicializované funkce. To je to, co je autoload. Zde je návod, jak dosáhnout toho, po čem jste.

Ve vašem ~/.my_zsh_functions Říkáte, že chcete dát funkci s názvem my_function, Která echos "hello world". Ale to zabalíte do volání funkce, což není způsob, jak to funguje. Místo toho musíte vytvořit soubor s názvem ~/.my_zsh_functions/my_function. V tom stačí dát echo "Hello world", Ne do funkčního obalu. Také byste mohli udělat něco takového, pokud opravdu chcete mít obal.

# ~/.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

Dále do souboru .zshrc Přidejte následující:

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

Když nainstalujete nový ZSH Shell, napište which my_function. Měli byste vidět toto:

my_function () {
    # undefined
    builtin autoload -XU
}

ZSH pro vás právě odstranil my_funkci pomocí autoload -X. Nyní spusťte my_function, Ale stačí zadat my_function. Měli byste vidět tisk Hello world A nyní, když spustíte which my_function, Měli byste vidět funkci vyplněnou takto:

my_function () {
    echo "Hello world"
}

Skutečná magie přichází, když nastavíte celou složku ~/.my_zsh_functions Tak, aby pracovala s autoload. Pokud chcete, aby každý soubor, který do této složky vložíte, fungoval takto, změňte to, co vložíte do svého .zshrc, Na něco podobného:

# 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