티스토리 뷰

tool 성격의 프로그램을 멋지게 개발해 배포하였을 때,
프로그램의 활용도와 사용자 편의성을 높이려면 명령어 자동 완성이 필요합니다.


자동완성의 예를 보자면,
우분투 터미널에서 "git" 명령을 입력하고 한 칸 띄고 Tab 키만 눌러도 사용 가능한 명령어 리스트가 뜹니다.
또 그중에 처음 몇 글자만 입력하고 Tab 키를 누르면 나머지 글자들이 자동으로 입력됩니다.
(이게 뜨지 않으면 생각만 해도 피곤한 일이 아닐 수 없습니다.)

 pic1. git의 명령어 자동완성

이런 자동 완성 기능을 bash에 적용하는 방법을 알아보겠습니다.
(이 말인 즉, bash를 사용하지 않는 OS에서는 적용이 안될 수도 있습니다.)

bash의 자동 완성 기능을 사용하려면 커서를 이용하는 script를 작성해야 합니다.
/etc/bash_completion.d 폴더를 보게 되면 이미 설치된 다른 프로그램들의 자동 완성 script들이 모여 있습니다.

 

 pic2. /etc/bash_completion.d directory 중 일부

여기에 있는 것들 중 짧은 녀석들을 보면 비슷하게 따라할 수 있을 듯 합니다.
용량을 보고 yum-arch를 살펴보려다가 지나치게 짧아서 "jar"라는 파일을 열어봤습니다.

 pic3. /etc/bash_completion.d/jar 파일 내용

이 정도면 사실상 웬만한 프로그램 자동 완성은 쉽게 응용할 수 있습니다.

제일 하단의 "complete -F _jar jar"는 문구가 중요합니다.
"jar" 라는 명령 후 사용자가 한 칸 띄고 Tab 키를 입력 했을 때,
"_jar" 함수를 불러주게 됩니다.

한번 Test를 해보겠습니다.

"order"라는 명령어가 있다 치고, 그에 대한 자동 완성을 작성해 보겠습니다.
(※이 포스팅에서는 최종적으로 햄버거 주문하는 자동 완성 shell을 만들겠습니다.)

bash_completion.d 디렉토리로 이동한 후, sudo vi로 "order"라는 파일을 생성합니다.

cd /etc/bash_completion.d
sudo vi order

#!/bin/bash
_order()
{
    echo 'auto completion test'
}
complete -F _order order

/etc/bash_completion.d/order


우선, 자동 완성이 잘 먹는지 안 먹는지만 Test해 봅니다.

예상 대로라면 $order <텝키> 를 했을 때,

"auto completion test" 라는 문구가 떠야합니다.


저렇게 파일을 작성한 후에 적용 시키기 위해서는 컴퓨터를 재부팅 하거나

수동으로 적용시켜야 합니다.

수동으로 특정 bash script를 적용하는 방법은 source <script> 인데요.


bash_completion.d 디렉토리 안의 파일들은 /etc/bash_completion 파일에서 로드하고 있으므로,

해당 파일에 source 명령을 적용하면 됩니다.


source /etc/bash_completion


적용이 잘 되었는지 Test 해보겠습니다.

shell에 "order"라고 입력 후 한칸 띄고 텝 키를 눌러봅니다.

(※"order" 명령 자체는 실제로 없는 명령이라 자동 완성 되지 않습니다.)

 

 pic4, pic5. 자동 완성 Test 1 결과

잘 되는 걸 확인했습니다.


이제 조금 더 실용적으로, argument를 다루는 Test를 해보겠습니다.

git의 경우도 "git "에서 텝 키를 누를 때와, "git checkout "에서 텝 키를 누를 때,

자동 완성 내용이 다릅니다.

이유는 argument 들에 따라 bash script로 체크해서 입력 값에 따라 다르게 가이드 하도록 짜여졌기 때문입니다.


위 "jar" 파일에도 보이듯이 미리 지정된 변수들이 있습니다.

$COMP_CWORD$COMP_WORDS 입니다.

이 변수 들은 우리가 C 프로그램에서 명령행 인자를 받을 때 사용하는

int main(int argc, char *argv[])

와 매칭됩니다.

$COMP_CWORD

int argc

 $COMP_WORDS

char *argv[]


실제로 $COMP_CWORD 에는 명령행에 현재 입력된 개수가 숫자 값으로 입력되어 있으며,

$COMP_WORDS에는 명령행 인자들이 srging 배열로 입력되어 전달됩니다.


출력 예제를 보겠습니다.

/etc/bash_completion.d/order 파일을 수정합니다.


#!/bin/bash
_order()
{
    echo ''
    echo 'argument count : '$COMP_CWORD
    echo 'argument words0 : '${COMP_WORDS[0]}
    echo 'argument words1 : '${COMP_WORDS[1]}
    echo 'argument words2 : '${COMP_WORDS[2]}
}

/etc/bash_completion.d/order


수정, 저장 후 반드시

source /etc/bash_completion

명령을 입력해 변경 내용을 적용 시킵니다.

 

 pic6. bash argument test

보시다시피, c의 명령행 인자와 동일하게 $COMP_WORDS의 배열 0번째는 자기 자신이 입력되는 모습입니다.


이제 argument의 원리를 이해했으니 이 내용을 활용해,
실질적인 자동 완성 text를 출력해보겠습니다.

본격적으로(?) order라는 명령을 입력 했을 때,
햄버거, 피자, 음료수를 자동 완성으로 제공하겠습니다.

#!/bin/bash
_order()
{
    local cur 

    COMPREPLY=()
    _get_comp_words_by_ref cur 

    if [ $COMP_CWORD = 1 ] 
    then
        COMPREPLY=( $( compgen -W 'hamburger pizza drink' -- "$cur" ) )
    fi  
}
complete -F _order order

 /etc/bash_completion.d/order


4,5,6 행의 "local cur" ~ "_get_comp_words..." 의미는 명확히 이해되지 않지만
저 라인들이 없으면, 나중에 명령어에서 텝 키를 눌렀을 때, 가이드는 뜨는데 단어가 자동으로 써지지는 않습니다.
일단 잘 모르면 다른 shell들도 다 넣었으니, 따라 넣습니다.

11행의 COMPREPLY 구문이 자동 완성을 제공하는 부분입니다.
해당 명령 라인의 의미에 대해서는 해석하지 않고 그냥 사용하도록 하겠습니다.
compgen -W 뒤에 '' 제공할 자동 완성 keyword 들을 한 칸씩 띄워 넣으면 됩니다.
(gnu의 bash compgen 명령 가이드 : 8.7 Programmable Completion Builtins)

적용 결과를 보겠습니다. (source /etc/bash_completion 명령은 계속 잊지 마세요)

 

 pic7. 자동 완성 Test 3

성공적으로 표시가 되었습니다.
제가 입력한 'hamburger pizza drink' 와 다르게, abc 순으로 정렬되어 출력 되는 걸 볼 수 있습니다.



이제, argument들을 활용 해, 상황 별 다른 자동 완성을 제공하도록 수정해 보겠습니다.

2번째, 명령 행으로 hamburger나 drink를 입력했을 때,
그 다음 자동 완성은 그에 맞는 다른 걸 제공하는 것이 자연스러울 것입니다.

다음과 같이 tree를 design해 보았습니다.

 pic8. 자동 완성 tree 구상

저의 경우는 단순하게 구현하기 위해 COMP_CWORD로 명령어 인자가 몇개 입력되었는지
우선 if문으로 구분 후,
중첩 if문으로 다시 판단해서 다른 자동 완성을 제공해 보았습니다.

물론 이게 최선은 아닙니다만, 간단한 자동 완성만 제공할 때에는
이런 형태가 단순하고 좋다고 생각합니다.
복잡한 자동 완성을 제공하실 때에는 bash의 다양한 문법들을 활용해,
정교하게 작성하시기 바랍니다.

#!/bin/bash
_order()
{
    local cur 

    COMPREPLY=()
    _get_comp_words_by_ref cur 

    if [ $COMP_CWORD = 1 ] 
    then
        COMPREPLY=( $( compgen -W 'hamburger pizza drink' -- "$cur" ) )
    elif [ $COMP_CWORD = 2 ] 
    then
        if [ "${COMP_WORDS[1]}" = "drink" ]
        then
            COMPREPLY=( $( compgen -W 'coke juice' -- "$cur" ) )
        elif [ "${COMP_WORDS[1]}" = "hamburger" ]
        then
            COMPREPLY=( $( compgen -W 'bulgogi cheese' -- "$cur" ) )
        fi  
    fi  
    return 0
}
complete -F _order order

 /etc/bash_completion.d/order 최종본

source /etc/bash_completion 으로 적용 후 결과를 Test해 봅니다.

 

 pic9. 자동 완성 최종본 Test



덧붙여...

모든 User에 관계 없이 적용되려면 위와 같이 /etc/bash_completion.d 를 이용함이 맞지만
특정 User에게만 제공되어도 되는 기능이면 굳이 저기에 넣을 필요는 없습니다.

저 directory에 shell을 넣으려면 root 권한이 필요한데,
상황에 따라 배포하는 Program이 install시에, 혹은 사용자 계정 자체에 root 권한이 없을 수도 있습니다.

이럴 때에는 별도 패키지 directory를 구성하고 그 안에 자동 완성 shell을 생성하시고,
~/.bashrc 파일의 마지막 라인에 "source <자동완성 shell parh>" 명령을 삽입 해
해당 User에 대해서만 적용 시킬 수 있습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함