Command Completion

명령에서 사용되는 인수나 옵션이 종류가 많거나 또는 이름이 길거나 하면 일일이 기억하기도 힘들고 오류 없이 입력하기도 어렵습니다. 이럴 때 tab 키를 이용한 자동완성 기능이 유용하게 사용되는데요. 명령문 자동완성 기능은 각 명령에서 자체적으로 제공하는 것은 아니고 complete, compgen, compopt 명령을 이용해 만들어 사용하는 것입니다. 자주 사용하는 명령에 자동완성 기능이 없다면 만들어서 추가할 수도 있고 이미 설정돼 있는 기능이라도 마음에 들지 않으면 삭제하고 재설정할 수도 있습니다.

# find 명령을 사용하기 위해 'find -' 까지 입력한 뒤에 tab 키를 누르면 
# 이용할 수 있는 옵션 목록을 한눈에 보여줍니다.       
$ find -[tab]
-amin                   -fprint0                -mmin                   -quit
-anewer                 -fprintf                -mount                  -readable
-atime                  -fstype                 -mtime                  -regex
-cmin                   -gid                    -name                   -regextype
-cnewer                 -group                  -newer                  -samefile
-context                -help                   -nogroup                -size
-ctime                  -ignore_readdir_race    -noignore_readdir_race  -true
-daystart               -ilname                 -noleaf                 -type
-delete                 -iname                  -nouser                 -uid
-depth                  -inum                   -nowarn                 -used
-empty                  -ipath                  -ok                     -user
-exec                   -iregex                 -okdir                  -version
-execdir                -iwholename             -path                   -warn
-executable             -links                  -perm                   -wholename
-false                  -lname                  -print                  -writable
-fls                    -ls                     -print0                 -xdev
-follow                 -maxdepth               -printf                 -xtype

자동완성을 위해 현재 등록돼 있는 명령 이름들은 complete builtin 명령을 통해 볼수있습니다.

$ complete -p find
complete -F _find find        # find 명령의 자동완성을 위해 _find 함수(-F) 가 사용됨

$ complete -p help
complete -A helptopic help    # help 명령의 자동완성을 위해 helptopic action(-A) 이 사용됨

Completion 은 명령문 스트링을 만드는 작업.

사실 자동완성과 명령은 관계가 없습니다. 무슨말이냐 하면 자동완성은 단지 명령문 스트링을 만드는 작업입니다. 다음은 실제 시스템에 'hello' 라는 명령은 없지만 자동완성을 설정하고 있는 예입니다.

# hello 를 위한 자동완성 단어들을 등록
$ completion -W "aaa bbb ccc ddd" hello

# 'hello ' 까지 입력하고 tab 키를 누르면 등록했던 단어들이 표시된다.
$ hello [tab]           
aaa bbb ccc ddd

# 'hello c' 까지 입력하고 tab 키를 누르면 'hello ccc' 명령문이 자동완성 된다.
$ hello c[tab]

# 자동완성 등록확인
$ complete -p hello 
complete -W 'aaa bbb ccc ddd' hello

기본적으로 사용할 수 있는 자동완성 이름들

위의 예에서는 -W wordlist 옵션을 이용해 직접 자동완성 단어를 등록해 사용했지만 completion 에서는 시스템 내에서 기본적으로 사용할 수 있는 여러 이름들을 카테고리 별로 분류하여 제공합니다. 이와 같은 이름들은 -A action 옵션을 이용해 사용할 수 있습니다.

# 자동완성으로 export action 을 사용
$ complete -A export  hello

# 'hello ' 까지 입력하고 tab 키를 누르면 현재 export 된 변수 이름들을 보여준다.
$ hello [tab]
ANT_HOME                  LC_ADDRESS                SCALA_HOME
CDPATH                    LC_IDENTIFICATION         SESSION
CLUTTER_IM_MODULE         LC_MEASUREMENT            SESSION_MANAGER
COLORTERM                 LC_MONETARY               SESSIONTYPE
DART_SDK                  LC_NAME                   SHELL
DBUS_SESSION_BUS_ADDRESS  LC_NUMERIC                SHLVL
...

다음은 -A 옵션으로 사용할 수 있는 action 입니다.

action 설명
-a | alias 현재 설정되어 있는 alias 이름
arrayvar array 변수 이름
binding Readline 에서 사용하는 key binding 이름.
-b | builtin Shell builtin 명령 이름.
-c | command $PATH 에의해 접근 가능한 시스템 전체 명령 (builtin 명령 포함).
-d | directory 현재 위치에서 디렉토리만 표시
FIGNORE 변수를 이용해 필터링 할 수 있습니다.
disabled Disable 된 shell builtin 명령 이름.
enabled Enable 된 shell builtin 명령 이름.
-e | export Export 된 shell 변수 이름.
-f | file 현재 위치에서 모든 파일 표시 (디렉토리, 심볼릭 링크 모두).
FIGNORE 변수를 이용해 필터링 할 수 있습니다.
function 현재 shell 에 설정되어 있는 함수 이름.
-g | group /etc/group 에 등록된 group 이름
helptopic Help builtin 명령으로 도움말을 볼수있는 이름들.
hostname /etc/hosts 에 등록된 호스트이름. (HOSTFILE 변수 설정도 포함).
-j | job background 로 실행중인 모든 job 이름.
-k | keyword Shell 키워드.
running Running jobs 이름.
-s | service /etc/services 에 등록된 service 이름.
setopt Set 명령에서 사용할 수 있는 옵션 이름.
shopt Shopt 명령에서 사용할 수 있는 옵션 이름.
signal Signal 이름.
stopped Stopped jobs 이름
-u | user /etc/passwd 에 등록된 user 이름.
-v | variable 현재 shell 에 설정되어 있는 모든 변수 이름.

Completion Options

자동완성 이름들을 생성하는 것 외에도 -o option 옵션 설정을 통해 completion 의 동작 방식을 변경할 수 있습니다. 이 옵션은 추후에 compopt 명령을 사용해 변경할 수 있습니다. 현재 설정된 옵션 상태는 compopt 명령이름 으로 확인할 수 있으며 - 로 표시된 것은 현재 설정되어있는 상태고 + 로 표시된 것은 설정이 안된 상태입니다

  • bashdefault

    자동완성 이름을 생성하지 못했을때 default Bash completions 을 사용하게 됩니다.

$ complete -o bashdefault -W '' hello

# bash 변수이름 자동완성
$ hello $BASH_[tab]
$BASH_ALIASES                $BASH_COMMAND                $BASH_SOURCE
$BASH_ARGC                   $BASH_COMPLETION_COMPAT_DIR  $BASH_SUBSHELL
$BASH_ARGV                   $BASH_LINENO                 $BASH_VERSINFO
$BASH_CMDS                   $BASH_REMATCH                $BASH_VERSION

# 패턴에 매칭되는 파일을 표시
$ hello Read*[tab]
ReadObject.class  ReadObject.java

$ hello Read*.j*[tab]
$ hello ReadObject.java    # 파일이름 완성
  • default

    자동완성 이름을 생성하지 못했을때 readline 기본 파일이름 완성을 사용하게 됩니다.

# 자동완성 이름이 하나도 생성되지 못하게 -W '' 설정
$ complete -W '' hello

$ hello [tab] # tab 키를 눌러도 아무것도 나타나지 않는다.

# 이번에는 '-o default' 옵션 추가
$ complete -o default -W '' hello

# 자동완성 이름을 생성하지 못하자 readline 기본 파일이름 완성을 사용.
$ hello [tab]
2013-03-19 154412.csv  music/                 video/                 
Address.java           ReadObject.class       WriteObject.class      
address.ser            ReadObject.java        WriteObject.java
  • dirnames

    자동완성 이름을 생성하지 못했을때 디렉토리 이름 완성을 사용하게 됩니다.

$ complete -o dirnames -W '' hello

# 자동완성 이름을 생성하지 못하자 디렉토리 이름 완성을 사용.
$ hello [tab]
music/                 video/
  • filenames

    자동완성 이름을 파일이름 으로 취급합니다. 그래서 이름에 공백이나 특수문자가 있을경우 자동으로 escape 해주고 동일한 이름의 디렉토리가 있을 경우는 뒤에 / 를 붙여줍니다.

$ complete -W 'hoo\ bar foo&bar music'  hello

# 자동완성 이름이 수정없이 그대로 완성된다.
$ hello hoo bar
$ hello foo&bar
$ hello music

# 이번에는 -o filenames 옵션 사용
$ complete -o filenames -W 'hoo\ bar foo&bar music'  hello

# 자동완성 이름을 파일로 취급하여 이름에 공백이나 특수문자가 있을경우 escape 해주고 
# 같은 이름의 디렉토리가 있을경우 '/' 를 붙여준다.
$ hello hoo\ bar
$ hello foo\&bar
$ hello music/
  • noquote

    기본적으로 파일이름을 완성할 때는 공백이나 특수문자를 escape 주는데 이 옵션을 설정하면 disable 됩니다.

# 현재 디렉토리 목록
$ ls
2013-03-19 154412.csv  address.ser  music/            ReadObject.java  WriteObject.class
Address.java           foo&bar.cvs  ReadObject.class  video/           WriteObject.java

# file action 을 사용하여 현재 디렉토리에 있는 파일이름을 자동완성으로 사용
$ complete -A file hello

# 기본적으로 파일이름은 공백이나 특수문자를 escape 해준다.
$ hello 2013-03-19\ 154412.csv
$ hello foo\&bar.cvs

# 이번에는 -o noquote 옵션 사용
$ complete -o noquote -A file hello

# 파일이름의 자동 escape 기능이 disable 된다. 
$ hello 2013-03-19 154412.csv
$ hello foo&bar.cvs
  • nospace

    이름을 완성하고 나면 다음 이름을 위해 공백을 띄우게 되는데요. 이 옵션은 그걸 방지합니다.

$ complete -W 'aaa bbb ccc'  hello

# 이름을 완성하고 나면 자동으로 공백을 띄운다.
$ hello aaa [stop] 

# 이번에는 -o nospace 옵션 사용
$ complete -o nospace -W 'aaa bbb ccc'  hello

# 이름을 완성하고 나서 공백을 띄우지 않는다.
$ hello aaa[stop]
  • plusdirs

    생성된 자동완성 이름에 디렉토리 이름 완성을 추가 합니다. 또한 자동완성 이름과 매칭되는 파일이 있을경우 공백, 특수문자를 escape 해줍니다.

# 현재 디렉토리 목록
$ ls
2013-03-19 154412.csv  address.ser  music/            ReadObject.java  WriteObject.class
Address.java           foo&bar.cvs  ReadObject.class  video/           WriteObject.java

# -o plusdirs 옵션 설정
$ complete -o plusdirs -W 'aaa bbb ccc' hello

# 자동완성 이름 aaa bbb ccc 외에 디렉토리 이름 완성이 추가 되었다
$ hello [tab]
aaa    bbb    ccc    music/ video/

-G globpat

globbing 을 자동완성 이름을 생성하는데 사용할 수 있습니다.

$ complete -G "*.java" hello

$ hello [tab]
Address.java      ReadObject.java   WriteObject.java

-X filterpat

이것은 생성된 자동완성 이름을 패턴을 이용해 필터링 하는 기능입니다.

$ complete -W 'aaa.x bbb.y ccc.z' -X "*.z" hello

$ hello         # '*.z' 패턴에 의해 'ccc.z' 가 필터링 되었다.
aaa.x  bbb.y

-P prefix, -S suffix

생성된 자동완성 이름 앞, 뒤에 prefix, suffix 를 붙일 수 있습니다.

$ complete -W 'aaa bbb ccc' -S "/" hello

$ hello 
aaa/  bbb/  ccc/

-D

자동완성이 설정되지 않은 명령들에게 default 로 적용하기 위해 사용

-E

명령행이 empty 상태일때 적용하기 위해 사용

자동완성 함수

위에서 여러가지 자동완성 이름을 생성하는 방법을 살펴보았지만 사실 주로 사용하는 방법은 -F 함수이름 을 이용하는 것입니다. 자동완성 함수는 일반 함수처럼 현재 shell 에서 실행되고 정의될 때 alias 가 확장됩니다. bash 에서만 사용되는 함수이므로 bash 가 가지는 여러 가지 확장 기능을 사용할 수 있습니다.

함수 내에서 자동으로 설정되는 변수들

$1 , $2 , $3 의 의미는 다음과 같습니다.

mycomm ubuntu fedora arch[tab]
   \            |        \
    \_ $1       |_ $3     \_ $2
  (command)  (previous)  (current)

current 가 " 문자로 시작할 경우 $2" 문자를 값으로 전달받지 못합니다. 그럴경우 ${COMP_WORDS[COMP_CWORD]} 를 이용하면 됩니다. 함수에서 주로 사용하는 변수는 다음과 같습니다.

  • COMP_WORDS

    현재까지 입력한 명령의 단어들을 값으로 가지고 있는 array 변수입니다. 위의 예에서는 COMP_WORDS[0]=mycomm, COMP_WORDS[1]=ubuntu, COMP_WORDS[2]=fedora, COMP_WORDS[3]=arch 가 됩니다.

  • COMP_CWORD

    현재 커서가 위치한 단어의 index 를 나타냅니다. 위의 예에서는 3 이 됩니다. $2${COMP_WORDS[COMP_CWORD]} 와 같고 $3${COMP_WORDS[COMP_CWORD-1]} 와 같게됩니다.

  • COMPREPLY

    Array 변수로, 함수를 통해 만들어진 자동완성 단어를 최종적으로 이 변수에 넣은 후 리턴하게 됩니다. 그럼 tab 키를 이용한 자동완성시에 이 변수의 값들이 자동완성 단어로 사용되게 됩니다.

compgen 명령

이 명령은 자동완성 이름을 생성하는데 사용됩니다. 앞서 소개한 complete 명령의 옵션들을 거의 동일하게 사용할 수 있습니다. 한가지 유용한 기능은 마지막에 word 를 인수로 주면 word 와 매칭되는 단어들만 선택되게 됩니다. 그러므로 주로 COMPREPLY 변수에 값을 저장하는 용도로 사용됩니다.

# 마지막에 word 인수를 주면 매칭되는 단어들만 나온다
$ compgen -W 'a111 b222 b333 c444' -- b
b222
b333

예제 )

다음은 간단하게 2 단계의 옵션을 처리하는 내용입니다. 우선 자동완성 함수는 shell script 가 아니기 때문에 파일 첫줄에 shebang line 은 필요하지 않습니다. 본문중에 사용되는 자동완성 단어는 직접 입력하였으나 꼭 그럴 필요는 없고 여러 유틸리티 프로그램을 활용해 생성할 수 있습니다. 최종적으로 COMPREPLY array 변수에 단어들을 넣어주고 return 하면 되는 것입니다.

다음 내용을 mycomp.sh 에 작성하였다면 source mycomp.sh 한 후 프롬프트 상에서 테스트해봅니다. 정상적으로 동작 되는 것을 확인하였으면 ~/.bash_completion 에 저장하여 사용하거나 /etc/bash_completion.d/ 에 놓고 사용하시면 됩니다.

파일 마지막에 complete 명령의 -F 옵션으로 hello 의 자동완성 함수를 등록하는 것을 볼 수 있습니다.

mycomp_() {

    local comm=$1
    local cur=$2
    local prev=$3

    local options="--fruit --planet --animal"
    local fruits="apple orange banana"
    local animals="lion tiger elephant"
    local planets="mars jupiter saturn"

    if [ ${COMP_CWORD} -eq 1 ]; then
        COMPREPLY=( $(compgen -W "$options" -- "$cur") )
        return
    fi

    if [ ${COMP_CWORD} -eq 2 ]; then
        case $prev in
            --fruit)
                COMPREPLY=( $(compgen -W "$fruits" -- "$cur") )
                ;;
            --animal)
                COMPREPLY=( $(compgen -W "$animals" -- "$cur") )
                ;;
            --planet)
                COMPREPLY=( $(compgen -W "$planets" -- "$cur") )
                ;;
        esac
        return
    fi
}

complete -F mycomp_ hello

Completion 활용

자동완성 기능을 활용하여 자주 사용하는 명령 옵션이나, 문장을 한번에 입력합니다.

# ~/.bashrc.d 디렉토리에 두어 쉘 실행시 source 명령으로 읽어 들입니다.
$ cat myOptions.sh
complete -W -fsyntax-only clang
complete -W '"-std=c++14 -fsyntax-only"' clang++
complete -W $'"\'BEGIN { }\'"' awk

Virtualbox Bash Completion

vboxmanage 같은 명령은 서브 명령과 옵션들이 많아 터미널에서 사용하기가 어려운데 자동완성 함수를 작성해 놓으면 편리하게 사용할 수 있습니다.

https://github.com/mug896/virtualbox-bash-completion