zsh에서 자신의 쉘 함수를 정의하고로드하는 방법


54

zsh에서 자체 쉘 기능을 정의하고 실행하는 데 어려움을 겪고 있습니다. 공식 문서 의 지침 을 따르고 간단한 예제를 먼저 시도했지만 작동시키지 못했습니다.

폴더가 있습니다 :

~/.my_zsh_functions

이 폴더 functions_1에는 rwx사용자 권한으로 불리는 파일이 있습니다 . 이 파일에는 다음과 같은 셸 함수가 정의되어 있습니다.

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이 대신 사용되며 이는 거의 모든 사람에게 올바른 값입니다.
Frank Terbeck

답변:


94

zsh에서 함수 검색 경로 ($ fpath)는 디렉토리 세트를 정의합니다. 디렉토리에는 포함 된 함수가 처음 필요할 때 자동으로로드되도록 표시 될 수있는 파일이 있습니다.

Zsh에는 두 가지 자동로드 파일 모드가 있습니다. 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 '는 함수가로드되는 동안 앨리어스 확장을 비활성화하고`z'는 어떤 이유로 든`KSH_AUTOLOAD '가 설정되어 있어도 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 () { ... }코드를 자유롭게 넣을 수 있지만 불필요합니다.

그러나 하나의 파일에 하나의 기능 만 포함 할 수 있다는 주장은 완전히 정확하지는 않습니다.

특히 함수 기반 완성 시스템 (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 % 안녕하세요
안녕하세요 세계.

이것이 문제를 해결하기를 바랍니다.

(이 모든 트릭을 사용하는보다 복잡한 실제 예제 중 하나는 zsh의 함수 기반 완료 시스템에서 이미 언급 한` _tmux '함수입니다.)


고마워요 프랭크! 다른 답변에서 파일 당 하나의 함수 만 정의 할 수 있다고 읽었습니다. 그렇습니까? my_function () { }귀하의 Hello world예 에서 구문 을 사용하지 않은 것으로 나타났습니다 . 구문이 필요하지 않은 경우 언제 사용하는 것이 좋습니까?
Amelio Vazquez-Reina 2016 년

1
나는 그 질문들을 다루기 위해 원래의 대답을 확장했다.
Frank Terbeck

"파일에서 파일처럼 이름이 지정된 함수를 항상 정의하고 파일 끝에서 해당 함수를 호출합니다."왜 그렇습니까?
Hibou57

Hibou57 : (제외 : 그것은 오타입니다. "기능을 정의해야합니다", 지금 수정되었습니다.) 다음 코드를 고려할 때 분명하다고 생각했습니다. 어쨌든, 나는 그 이유를 조금 더 문자 그대로 설명하는 단락을 추가했습니다.
Frank Terbeck

여기에 귀하의 사이트 덕분에 더 많은 정보를 원하시면입니다.
Timo

5

fpath요소에 의해 명명 된 디렉토리의 파일 이름은 그것이 정의한 자동로드 가능 함수의 이름과 일치해야합니다.

함수의 이름이 지정 my_function되고 사용자 ~/.my_zsh_functions의 의도 된 디렉토리 fpath이므로 정의 my_function는 파일 에 있어야합니다 ~/.my_zsh_functions/my_function.

제안 된 파일 이름 ( functions_1) 에서 복수는 파일 에 여러 기능을 넣을 계획임을 나타냅니다. 이것은 방법 fpath과 자동 로딩 이 아닙니다 . 파일 당 하나의 함수 정의가 있어야합니다.


2

source ~/.my_zsh_functions/functions1터미널에 제공 하고 평가 my_function하면 이제 함수를 호출 할 수 있습니다


2
감사합니다,하지만의 역할을 무엇인가 FPATH하고 autoload다음? 왜 파일을 소싱해야합니까? 내 업데이트 된 질문을 참조하십시오.
Amelio Vazquez-Reina

1

$ ZDOTDIR / .zshrc의 모든 기능을 가진 파일을 다음과 같이 "로드"할 수 있습니다.

source $ZDOTDIR/functions_file

또는 점 "."을 사용할 수 있습니다. "소스"대신.


1
감사합니다,하지만의 역할을 무엇인가 FPATH하고 autoload다음? 왜 파일을 소싱해야합니까? 내 업데이트 된 질문을 참조하십시오.
Amelio Vazquez-Reina

0

소싱은 초기화 된 함수를 지연시키는 것이기 때문에 소싱은 확실히 올바른 접근법이 아닙니다. 그게 다야 autoload. 당신이 추구하는 것을 성취하는 방법은 다음과 같습니다.

에서 "hello world"를 에코 ~/.my_zsh_functions하는 함수를 넣고 싶다고 말합니다 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"
}

이제 전체 ~/.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.