커맨드 라인 명령의 탭 완성 구현하기


발생일: 2013.01.24

문제:
얼마 전 만든 커맨드라인 영어사전인 ndic 모듈에 새로운 기능을 붙여보려고 한다.

생각해둔 건 탭 자동완성 기능이다.

커맨드라인에서 단어를 검색하다보니,
입력하던 도중에 철자를 틀리거나, 단어의 뒷 부분을 까먹어서 제대로 검색되지 않는 경우가 있더라.

`ndic symp<TAB>` 

다른 커맨드라인 명령어에서처럼,
ndic <단어> 명령 이후에 탭을 입력했을 때 비슷한 단어 목록을 제시해주면 꽤 쓸만할 것 같다.

기존 명령어의 탭 자동완성은 주로 폴더 내 파일 목록을 보여주는데,
위와 같이 커스텀 명령에 적용하면서, 목록을 다이나믹하게 생성할 수도 있을까?


해결책:
요즘 많이 사용 하는 BASH Shell과 Z Shell의 방법이 다르긴 하지만, 둘 다 가능하다.
여기서는 일단 BASH에 관해서만 정리했다.

BASH의 빌트인 커맨드 중에 `compgen` `complete`를 활용하면,
기존의 커맨드 뿐 아니라 커스텀 커맨드에도 탭 자동완성을 적용할 수 있다.

자세한 건 포스트의 마지막 부분의 튜토리얼 문서를 참고하면 되고,
여기선 아래 샘플 코드를 보면서 단계적으로 설명해보려고 한다.

아래는, myprog란 명령어 이후에 탭을 입력하면 사용자 이름이 자동완성으로 출력되도록 한 코드이다.

  _myprog() {
    local curw # [1]
    COMPREPLY=() # [2]
    curw=${COMP_WORDS[COMP_CWORD]} # [3]
    COMPREPLY=($(compgen -A user -- $curw)) # [4]
    return 0 # [5]
  }

  complete -F _myprog myprog # [0]


[0] : myprog라는 명령어에서 탭 입력 시 _myprog 함수를 실행한다.
[1] : curw (current word)라는 지역 변수를 선언하고,
[2] : COMPREPLY 배열을 초기화한다.
        지금과 같이 탭 입력 시 함수를 호출하도록 하는 경우,(complete의 -F 옵션)
        함수 종료 후 COMPREPLY 배열의 값을 출력한다.
[3] : 커서가 있는 위치의 단어를 curw 에 할당한다.
        curw 값에 COMP_WORDS 배열에서 COMP_CWORD 인덱스에 해당하는 값을 할당한다.
        COMP_WORDS 는 커맨드라인에 입력된 각 단어의 배열이고,
        COMP_CWORD 는 현재 커서가 가리키는 단어의 인덱스를 가리킨다.       
[4] : 사용자 목록 중에서 curw 값과 매칭되는 것들을 COMPREPLY 배열에 담는다.
        compgen : 자동완성 목록을 준비하는데,
        -A user : 전체 유저 목록에서,
        -- $curw : curw 변수에 할당된 것과 매칭되는 목록을 리턴한다.
        $(compgen ...) : compgen을 수행한 결과를,
        COMPREPLY=( $(..) ) : 배열에 담아 COMPREPLY에 할당한다.
[5] : 정상적으로 종료한다.


주의할 것은, 이 코드는 배시가 초기화될 때 실행되어야 한다는 것!!
위 코드를 .bash_profile이나 .bash_rc에 추가해야 한다.
(.bash_profile에 대한 자세한 내용은 이전 포스트: bash_profile 을 참고)


좀 더 자세한 사용법을 알아보려면,

1. `man bash`를 실행해 나오는 배시 가이드에서 'compgen' 이나 'complete'를 검색해 확인해본다.
    (배시의 빌트인 명령어라 man compgen을 해도 가이드가 출력되진 않는다)
    온라인에서도 매뉴얼을 찾을 수 있다.

2. 위 샘플 코드를 포함를 포함해 커스텀 탭 구현하기에 대한 설명이 담긴 포스트

3. 데비안(Debian) 운영체제에서는 좀 더 사용하기 쉬운 bash_completion이 내장되어 있다 한다.
    사용해보지는 않았으나, 아래 튜토리얼을 참고하면 될 듯!

4. 노드(node.js)로 작성한 모듈에 탭 자동 완성을 구현하려고 한다면 REPL 모듈을 활용하면 된다.


덧)
탭 완성 기능을 구현하려면 .bash_rc 나 .bash_profile 에 추가해야 하는데,
모듈 설치 시 .bash_profile에 임의로 추가해 넣자니 좀 께름칙하다.
일반 명령 실행 시점에서 초기화하고 싶은데, 이 시점에서는 현재 쉘에 적용되지 않는다.
쉘의 프로필을 갱신하는 명령이 있긴 하지만,
역시 임의로 .bash_profile에 추가하는 게 내키지 않아 아직은 적용하지 않았다.
좀 더 깔끔한 방법이 없을까...?


카테고리

분류 전체보기 (728)
About me. (6)
Daylogs (693)
영어공부 (0)
My works - 추억 (29)
비공개 (0)