[shell] 명령어 자동완성 구현하기

alirz-pixel·2023년 2월 2일
0

linux

목록 보기
3/4

bash의 내장 명령어인 complete는 지정된 명령어에 대해 자동완성을 제공해준다. (tab키로 사용가능)

complete 옵션

-p <name>: <name> 명령어에 등록된 자동완성 목록을 보여준다. (<name> 생략시 모든 목록 출력)
-r <name>: <name>에 설정된 자동완성을 제거한다.
-W <wordlist> <name>: <name>에 설정할 <wordlist>(단어 목록)을 지정한다.
-F <function> <name>: <name>에 설정할 <functon>(함수)을 지정한다.

-W

-W <wordlist>를 통해 test(임시 명령어)에 --help, --horizon, --version에 대한 자동완성을 지원하도록 설정하였다.

IFS

<wordlist>를 정의할 때 단어들은 $IFS값에 따라 분리되며, 기본값은 <space><tab><newline>으로 되어있다.

만약 <wordlist>를 multiple words로 구성하고 싶다면, IFS 값을 아래와 같이 설정하면 된다.

_foo() {
	local IFS=$'\n'
	local words="
--help: 도움말
--version: 현재 foo의 버전을 나타냄
--test: 기본값으로 foo를 호출함
"
	COMPREPLY=($(compgen -W "$words" -- "$2"))
}
complete -F _foo foo

-F

# .bashrc
_foo() {
	COMPREPLY=($(compgen -W "--help --version --horizon" -- "$2"))
}
complete -F _foo foo

자동완성 함수를 지정하기 위해서 사용되며, 자동완성 함수는 일반 함수와 같고 정의될 때 alias가 확장된다.


자동완성 함수에서 사용되는 변수

기본적으로 $1, $2, $3 가 지정되고 의미는 아래와 같다.

foo --version --help --horizon
  \              |         \
   \_ $1         |_$3       \_$2
(command)    (previous)   (current)

COMP_WORDS

현재까지 입력한 명령 라인의 인수들을 값으로 가지고 있는 array 변수이다.
위의 예시에서는 COMP_WORDS[0]=foo, COMP_WORDS[1]=--version, COMP_WORDS[2]=--help, COMP_WORDS[3]=--horizon가 된다. (c언어의 argv랑 같다고 보면 된다)

COMP_CWORD

현재 커서가 위치한 인수의 index를 나타내며, 위에서 적은 예시에서는 3이 된다.

+tip

$2 == ${COMP_WORDS[COMP_CWORD]}
$3 == ${COMP_WORDS[COMP_CWORD - 1]}

이 값들($1, $2, $COMP_CWORD)은 커서의 현재 위치에 따라 달라진다.

COMP_LINE

현재 프롬프트 상에서 작성 중인 명령 라인 전체를 나타낸다.

COMP_POINT

명령 라인에서 현재 커서가 위치한 문자수 index를 나타낸다.

COMPREPLY

tab 키를 눌렀을 때 보여지는 내용을 담는 array 변수이다. 값이 출력될 때는 자동으로 중복이 제거되고 sort되어 출력된다.


자동완성 줄바꿈해서 출력하기

$ test --[teb][tab]
--help: 도움말
--version: 현재 foo의 버전을 나타냄
--test: 기본값으로 foo를 호출함
$ test --

자동완성을 위와 같이 줄바꿈해서 출력하고 싶다면, bind "set completion-display-width 0" 명령을 이용하면 된다.

모든 명령어에 적용

모든 명령어에 자동완성 목록이 줄바꿈이 되는 것을 원한다면 ~/.inputrc에 아래와 같이 적어주면 된다.

# ~/.inputrc
set completion-display-width 0

특정 명령어에만 적용

특정 명령어에서만 적용하고 줄바꿈을 적용하고 싶다면, 아래와 같이 해주면 된다.

_test() {
    local IFS=$'\n'
    local words="
--help: 도움말
--version: 현재 foo의 버전을 나타냄
--test: 기본값으로 foo를 호출함
"

    # 이 명령어에만 줄바꿈을 설정하고, 원래대로 되돌리기 위해서 현재 설정된 width를 가져옴
    local width=$(bind -v | sed -n 's/^set completion-display-width //p')

    if [[ $width -ne 0 ]]; then
        # 자동완성 줄바꿈 설정
        bind "set completion-display-width 0"
        # width 값을 이전 값으로 되돌림
        PROMPT_COMMAND="bind 'set completion-display-width $width'"
    fi

    COMPREPLY=($(compgen -W "${words}" -- "$2"))
}
complete -F _test test

2단계 옵션 처리하기

_git() {
    local width=$(bind -v | sed -n 's/^set completion-display-width //p')

    if [[ $width -ne 0 ]]; then
        bind "set completion-display-width 0"
        PROMPT_COMMAND="bind 'set completion-display-width $width'"
    fi

    local IFS=$'\n'
    local options="
push: main branch의 커밋을 원격 저장소로 올리기
reset: HEAD의 포인터를 특정 위치로 옮김
"
    local pushs="
-f: 원격 저장소의 내용을 강제로 현재 branch의 내용으로 덮어씀
-u: upstream 브랜치 연결 설정
"
    local resets="
--hard: 돌아간 커밋 이후의 변경 이력을 모두 삭제
--soft: 변경 이력은 모두 삭제하지만, 변경 내용은 유지되며 stage에 남음
"

    if (( COMP_CWORD == 1 )); then
        COMPREPLY=( $(compgen -W "$options" -- "$2") )
        return
    fi

    if (( COMP_CWORD == 2 )); then
        case $3 in
          reset)
            COMPREPLY=($(compgen -W "$resets" -- "$2"))
            return ;;
          push)
            COMPREPLY=($(compgen -W "$pushs" -- "$2"))
            return;;
        esac
    fi
}
complete -F _git git


더 많은 정보

https://mug896.github.io/bash-shell/command_completion.html

0개의 댓글