Egloos | Log-in
F/OSS study
F/OSS study
태그 : bash
2011/06/28   [bash-completion] shell 자동 완성 기능 확장하기 [2]
[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)
◀ 이전 페이지 다음 페이지 ▶

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

최근 등록된 덧글
informsi yang bagus dan be..
by pordanaia at 08/05
Informsi yang bagus dan b..
by jamalsu at 08/01
as inforasi yang menarik http..
by jamalsu at 07/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