Egloos | Log-in
F/OSS study
F/OSS study
[bash-completion] shell 자동 완성 기능 확장하기
bash: 4.1.5
bash-completion: 1.1


bash를 비롯한 최신 shell들은 사용자 입력을 줄이기 위해 여러 자동 완성 기능을 제공하며
bash의 경우 bash-completion 프로젝트에서 기본적인 파일/디렉터리 명은 물론
각 프로그램 별로 적절한 옵션이나 인자에도 이러한 자동 완성 기능을 사용할 수 있도록 지원하고 있다.

뿐만 아니라 간단한 쉘 프로그래밍을 통해 자신이 원하는 대로 기능을 확장할 수 있으므로
자주 쓰는 기능이 빠져있다거나 새로운 프로그램을 작성했을 때 이를 쉽게 추가/수정할 수도 있다.

기본적으로 bash는 자동 완성을 위해 여러 내부 변수 및 함수들을 제공한다.
COMP_WORDS라는 array 변수는 현재까지 명령행에 입력된 모든 단어들을 저장하고 있으며
이 배열의 크기는 COMP_CWORD라는 변수에 역시 저장되어 있다.
따라서 이를 이용해 현재 사용자가 어떤 단어를 입력하고 있는지 알아낼 수 있다.

예를 들어 현재 사용자가 "git log --o"이라는 명령을 입력한 상태에서 TAB 키를 누른다면
COMP_CWORD에는 '2'가 (일반적인 프로그래밍의 경우와 다르다!)
COMP_WORDS에는 각각 'git', 'log', '--o'라는 값이 저장될 것이다.
(따라서 이를 ${COMP_WORDS[0]}, ${COMP_WORDS[1]}, ${COMP_WORDS[2]}와 같이 접근할 수 있다.)

bash 내장 명령인 complete는 자동 완성이 수행되는 방법을 지정하는데
몇 가지 옵션이 있지만 대부분의 경우 -F 옵션을 통해 인자로 주어진 이름의 명령어의 옵션을
알아내는 함수를 수행하도록 하고 있다.

예를 들어 전에 살펴본 zlib-cat이라는 프로그램에 대한 자동 완성 기능을 추가하려면
관례적으로 앞에 '_' 기호가 붙은 _zlib_cat 함수를 작성한 뒤 다음과 같이 호출하면 된다.
(물론 중간의 '-' 기호도 '_' 기호로 변경하였다.)

complete -F _zlib_cat zlib-cat

실제 자동 완성 기능을 담당하는 (_zlib_cat) 함수는
위에서 주어진 정보를 기반으로 하여 필요한 작업을 수행한 뒤 자동 완성에 사용할 최종 결과를
COMPREPLY라는 array 타입의 변수에 저장해야 하는 책임을 가진다.

이 때 주로 compgen이라는 bash 내장 명령을 사용하게 되는데
-W 옵션을 통해 자동 완성이 가능한 전체 목록을 넘겨주고 현재 입력 중인 단어를 인자로 넘기면
자동으로 COMPREPLY에 알맞는 형태의 문자열을 stdout에 출력한다.

bash-completion에서 기본적으로 제공하는 파일들은
/etc/bash_completion과 /etc/bash_completion.d 디렉터리 아래의 파일들이다.
이를 참고해서 자신의 홈디렉터리에 .bash_completion 파일을 만들어두면
로그인 시에 자동으로 이를 읽어들이게 되므로 자신 만의 확장 기능을 정의할 수 있다.

가장 단순한 경우부터 살펴보기로 하자.
앞서 언급한 zlib-cat이라는 프로그램은 -h, -v, --help, --version 이라는
네 가지 옵션을 받을 수 있는데 -h 및 -v와 같은 한 글자 짜리 옵션은 자동 완성의 의미가 거의 없으므로
--로 시작하는 긴 이름의 옵션에만 자동 완성을 적용하려고 한다.

홈 디렉터리의 .bash_completion 파일을 만들어서 다음과 같이 입력한다.

#
# ~/.bash_completion:
#       my bash completion scripts
#

_zlib_cat()
{
    local cur=$(_get_cword)
    COMPREPLY=()

    case "$cur" in
        --*)
            COMPREPLY=( $(compgen -W "--help --version" -- $cur) )
            ;;
    esac
}

complete -F _zlib_cat zlib-cat

(당연하게도) #로 시작하는 부분은 주석이므로 건너뛰자.
_zlib_cat 함수의 제일 처음 두 줄은 거의 관례적으로 사용되는 패턴이다.

local은 지역 변수를 선언하는 것으로 cur라는 변수를 선언하고
초기값은 (/etc/bash_completion 파일에 정의된) _get_cword라는 함수의 결과로 설정한다.
(shell script에서 변수에 값 대입시 '=' 기호 좌우에 *공백이 없어야 함*에 주의하자!)

cur 변수는 현재 입력 중인 단어를 나타내는 것으로
위에서 설명한 대로 ${COMP_WORDS[COMP_CWORD]}의 형태로도 알아낼 수 있지만
미묘한 corner case를 피해가기 위해서는 위의 함수를 사용하는 것이 더 안전하다.
(최신 버전의 bash_completion을 사용한다면 _init_completion() 함수를 통해
cur, prev, words, cword 등의 지역 변수를 초기화하는 방식이 더 좋을 것이다.)

앞서 말했듯이 COMPREPLY라는 변수는 결과를 저장하는 용도로 사용하는데
안전을 위해(?) 초기값은 빈 값으로 설정해 둔다.
또한 COMPREPLY 변수는 array 타입이므로 '()' 기호가 필요하다는 것에도 유의하자.

여기서는 오직 현재 입력 중인 단어 (즉, $cur 변수의 값)에만 관심을 두는데
--로 시작하는 경우에만 실제 자동 완성을 시도하게 될 것이다.
(case는 간단히 C 언어의 switch와 비슷한 개념이라 생각하면 된다.
하지만 문자열에도 적용할 수 있으므로 훨씬 강력한 기능이다.)

이 경우 COMPREPLY에는 compgen 명령의 출력 결과가 저장될 것인데
-W 인자로 넘긴 목록은 --help와 --version 뿐이므로
이 안에서 자동 완성이 가능한 것이 있는지 판단하여 결과를 만들어 낸다.

여기까지 완성했다면 실제로 테스트해 볼 수 있다.
(물론 zlib-cat이라는 프로그램이 PATH 내에 있어야 한다.. ;;)
새로운 shell을 띄우고 터미널에서 다음과 같이 입력해 보자. (<TAB>은 TAB 키 입력을 의미)

$ zlib-cat --<TAB><TAB>
--help     --version
$ zlib-cat --h<TAB>
$ zlib-cat --help

위의 경우 --로 시작하는 옵션들은 자동 완성이 적용되지만
그 외의 인자 (즉, 파일 이름)에는 자동 완성이 적용되지 않을 것이다.
이러한 경우도 처리하기 위해서는 위의 case 문에
다음과 같이 default case를 처리하기 위한 부분을 추가하면 된다.
(diff -u 명령의 출력과 비슷하게 + 기호로 시작하는 줄이 추가된 내용이다.)

    case "$cur" in
        --*)
            COMPREPLY=( $(compgen -W "--help --version" -- $cur) )
            ;;
+       *)
+           compopt -o nospace
+           _filedir
+           ;;
    esac
}

우선 _filedir은 /etc/bash_completion 파일에서 제공하는 함수로
파일 및 디렉터리 이름에 대한 자동 완성을 구현한 것이다.
하지만 이 경우 완성된 파일/디렉터리 이름 뒤에 자동으로 공백 문자가 추가되었기 때문에
이를 방지하기 위해 앞서 (compopt 명령을 통해) nospace 옵션을 추가하였다.

이 정도 만으로도 간단한 프로그램의 경우 자동 완성 기능을 제공하는데 무리가 없을테지만
복잡한 옵션을 처리한다거나 특정 subcommand에 따라 이 후의 옵션이 달라지는 경우라면
COMP_WORDS 변수의 내용을 더 살펴본다거나 다른 외부 명령의 실행 결과를 이용하여
복잡한 함수를 작성할 수도 있을 것이다.
(궁금하다면 참고 문헌의 첫 번째 문서에서 part2 부분을 살펴보기 바란다.)


=== 참조 문헌 ===

by namhyung | 2011/06/28 01:50 | Tips | 트랙백 | 덧글(2)
트랙백 주소 : http://studyfoss.egloos.com/tb/5544729
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by SY Kim at 2011/06/28 23:58
이런 좋은 기능이 있는지 정말 몰랐네요. 좋은 글 감사합니다.
Commented by namhyung at 2011/06/29 20:44
도움이 되셨다니 다행입니다. ^^

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
elf CARM blktrace C git block-layer algorithm computer-architecture documentation memory kernel scm gcc vcs CAaQA3 sed binutils compiler synchronization perf linux awk glibc build script x86 bash SMP patch emacs
전체보기
이글루 파인더

최근 등록된 덧글
informsi yang bagus dan b..
by agen qnc at 06/22
informasi yang bagus dan b..
by agen qnc at 06/22
informasi yang bagus dan b..
by agen qnc at 06/22
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X