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                    # 전체 목록을 출력
. . .

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

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

현재 설정되어 있는 자동완성을 삭제할 때는 -r 옵션을 사용합니다.

$ complete -p kill
complete -F _kill2 kill

$ complete -r kill

$ complete -p kill
bash: complete: kill: no completion specification

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 입니다. 옆에 short 옵션이 함께 있는 경우는 두 가지로 설정이 가능합니다. 예를 들어 alias 를 설정할 경우 complete -A alias 도 되고 complete -a 로도 설정할 수 있습니다.

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 변수를 이용해 필터링 할 수 있습니다.
compgen 명령의 경우 대상 디렉토리를 지정할 수 있습니다.
compgen -f /some/dir/
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 상태일 때 적용하기 위해 사용

-I

명령 이름( initial word ) 에 적용하기 위해 사용

자동완성 함수

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

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

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

mycomm ubuntu fedora arch[tab]
   \            |        \
    \_ $1       |_ $3     \_ $2
  (command)  (previous)  (current)
  • 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]} 와 같게 됩니다.

  • COMP_LINE

    현재 프롬프트 상에서 작성 중인 명령 라인 전체를 나타냅니다.

  • COMPREPLY

    array 변수로, 여러 방법을 통해 만들어진 자동완성 단어를 최종적으로 이 변수에 넣은 후 리턴하게 됩니다. 그럼 tab 키를 이용한 자동완성시에 이 변수의 값들이 사용됩니다. 값이 입력될 때는 자동으로 중복이 제거되고 sort 되어 입력됩니다.

current 가 " 문자로 시작할 경우 $2" 문자를 값으로 전달받지 못합니다. 그럴경우 ${COMP_WORDS[COMP_CWORD]} 를 이용하면 됩니다.

compgen 명령

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

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

예제 )

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

다음 내용을 mycomp.sh 에 작성하였다면 source mycomp.sh 한 후 프롬프트 상에서 테스트해봅니다. 파일 마지막에 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 = 1 ]; then
        COMPREPLY=( $(compgen -W "$options" -- $cur) )
        return
    fi

    if [ $COMP_CWORD = 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

2.

이번 예제는 kill 명령을 사용할때 먼저 PID 를 검색할 필요없이 자동완성을 이용해 프로세스 목록을 보여주는 기능입니다. 기존 kill 명령은 신호이름만 자동완성이 되는데 PID 목록도 볼수있으면 편리하겠죠?

_kill2() 
{
    local CUR=${COMP_WORDS[COMP_CWORD]} WORDS
    # 자동완성 함수는 현재 shell 에서 실행되므로 영향을 미치지 않으려면
    # local 로 설정해 사용해야 합니다.
    local IFS=$' \t\n'

    case $CUR in
        -*) # '-' 문자로 시작하는 신호값 자동완성 
            WORDS=$( kill -l | sed -Ez 's/[0-9]+\) SIG/-/g' )
            COMPREPLY=( $(compgen -W "$WORDS" -- $CUR) )
            ;;
        *)
            # PID 목록은 자동완성할 것이 아니므로 앞에 번호를 붙여서 출력합니다.
            # 번호를 붙이지 않으면 자동완성이 적용되어 목록이 제대로 표시되지 않습니다.
            WORDS=$( ps axh -o pid,user,comm | gawk 'BEGIN {IGNORECASE=1} 
            $3~/'"$CUR"'/{a[i++]=$0} END{ 
            if (isarray(a)) {
                if (length(a) == 1) print a[0]
                else { 
                    len=length(i)
                    for (i in a) 
                        printf "%0*d) %s\n", len, i+1, a[i]
                }
            }}')
            # COMPREPLAY array 변수에 자동완성 단어가 설정될때 IFS 값에따라 분리가 됩니다.
            # 여기서는 PID 와 명령을 같이 보여줘야 하므로 IFS 값을 newline 으로 설정합니다.
            IFS=$'\n'
            COMPREPLY=( $WORDS )
            ;;
    esac
}

complete -F _kill2 kill

3.

이번에는 2번 예제를 가지고 프로세스 목록이 출력됐을때 번호를 입력하면 해당 PID 와 명령이름이 입력되는 것을 해보겠습니다. 이전 검색 결과를 사용하기 위해서는 global 변수에 저장해 놓는것이 필요하기 때문에 여기서는 함수명과 동일한 변수이름을 사용하였습니다.

_kill3() 
{
    local CUR=${COMP_WORDS[COMP_CWORD]} WORDS
    local IFS=$' \t\n'

    case $CUR in
        -*)
            WORDS=$( kill -l | sed -Ez 's/[0-9]+\) SIG/-/g' )
            COMPREPLY=( $(compgen -W "$WORDS" -- $CUR) )
            ;;

        # current 그러니까 현재 커서가 위치한 곳의 값이 숫자인 상태에서 tab 키를 눌렀을경우
        # 이전에 $_kill3 변수에 저장해 놓은 목록을 이용해 PID 와 명령이름을 출력합니다.
        [0-9] | [0-9][0-9] | [0-9][0-9][0-9])
            CUR=$(( 10#$CUR ))   # 8 진수로 인식되는 것을 방지하기 위해 10 진수로 변경
            COMPREPLY=( "$(echo "$_kill3" | gawk $CUR' == $1+0 {print $2,$4; exit}')" )
            ;;

        *)
            WORDS=$( ps haxo pid,user,comm | gawk 'BEGIN {IGNORECASE=1} 
            $3~/'"$CUR"'/{a[i++]=$0} END{ 
            if (isarray(a)) {
                if (length(a) == 1) print a[0]
                else { 
                    len=length(i)
                    for (i in a) 
                        printf "%0*d) %s\n", len, i+1, a[i]
                }
            }}')

            # 생성된 프로세스 목록을 global 변수인 $_kill3 에 저장해놓습니다.
            _kill3=$WORDS

            IFS=$'\n'
            COMPREPLY=( $WORDS )
            ;;
    esac
}

complete -F _kill3 kill

4.

clang 은 gcc 와 달리 기본적으로 자동완성이 설정되어 있지 않은데 여기서 설정해 보도록 하겠습니다.
clang 은 --autocomplete 옵션을 통해서 자동완성 기능을 지원하고 있습니다.

# '-std' 스크링과 매칭되는 옵션을 출력
$ clang --autocomplete=-std
-std:   Language standard to compile for
-std=   Language standard to compile for
-stdlib=        C++ standard library to use

$ clang --autocomplete=-stdl
-stdlib=        C++ standard library to use

# '-stdlib=' 옵션에서 사용 가능한 값을 출력
$ clang --autocomplete=-stdlib=
libc++
libstdc++
platform

자동완성에서 current ($2), previous ($3) 값이 설정되는 것은 IFS 값을 따르는 것이 아니고 $COMP_WORDBREAKS 변수값에 따릅니다. 따라서 스트링이 공백 없이 붙어 있어도 아래의 문자 값에서 단어가 분리됩니다. 그러므로 예를 들어 -std=[커서] 상태에서 tab 키를 누르면 = 문자와 -std 값을 참조해서 자동완성 단어를 생성할 수 있습니다.

$ echo -n "$COMP_WORDBREAKS" | od -tax1
0000000  sp  ht  nl   "   '   >   <   =   ;   |   &   (   :
         20  09  0a  22  27  3e  3c  3d  3b  7c  26  28  3a

# current: =, previous: -std
$ clang -std=[tab]

# current: c++, previous: =
$ clang -std=c++[tab]

COMPREPLY 변수에 입력되는 $WORDS 값이 null 이면 complete 등록시 사용된 -o default 설정이 적용됩니다.

_clang() 
{
    local CMD=$1
    local CUR=${COMP_WORDS[COMP_CWORD]}
    local PREV=${COMP_WORDS[COMP_CWORD-1]} 
    local PREV2=${COMP_WORDS[COMP_CWORD-2]} 
    local WORDS

    case $CUR in
        --*)  # '--' 로 시작되는 옵션
            WORDS=$( $CMD --autocomplete="$CUR" | gawk '$1 ~ /^--/{print $1}' )
            ;;
        -*)   # '-' 로 시작되는 옵션
            WORDS=$( $CMD --autocomplete="$CUR" | gawk '$1 ~ /^-[^-]/{print $1}' )
            ;;
        =)
            # current 값이 '=' 문자일 경우 '$PREV=' 에서 사용할 수 있는 값을 출력합니다.
            WORDS=$( $CMD --autocomplete="$PREV=" | gawk '{print $1}' )
            # 값이 하나라는 것은 자기 자신을 말하므로 추가 입력되는 것을 방지하기 위해 WORDS="" 설정
            [ `echo "$WORDS" | wc -l` = 1 ] && { WORDS=""; compopt +o default +o bashdefault ;}
            COMPREPLY=( $WORDS )
            return
            ;;
        *)
            # 현재 '-std=c[tab]' 와같은 상태일 경우,'$PREV2' 는 '-std'
            if [ "$PREV" = '=' ]; then
                WORDS=$( $CMD --autocomplete="$PREV2=" )
            else 
                # 그외 값들 예를 들어 '-meabi [tab]'
                # exit 부분은 '-c [tab]' 했을때 -c 값이 중복 입력되는 것을 방지
                WORDS=$( $CMD --autocomplete="$PREV" | gawk '$1 ~ /^-/{exit}{print $1}' )
            fi
            ;;
    esac

    COMPREPLY=( $(compgen -W "$WORDS" -- $CUR) )

    # bash 는 기본적으로 tab 키를 누르면 자동완성 단어를 입력하고 space 를 띄우는데
    # 완성할 단어가 'string=' 형식일경우 space 를 띄우지 않게합니다.
    [ "${COMPREPLY: -1}" = "=" ] && compopt -o nospace
}

# COMPREPLY 값이 없어서 자동완성할 단어가 없을 경우 디폴트로 default, bashdefault
# 옵션이 적용되게 하고 clang, clang++ 두개의 명령에 _clang 함수를 설정
complete -o default -o bashdefault -F _clang clang clang++

5.

다음은 Dart 언어용 자동완성입니다. ${COMP_LINE} 변수 활용하는 것을 볼 수 있습니다.

_dart() 
{
    local COM=${COMP_WORDS[0]}
    local CUR=${COMP_WORDS[COMP_CWORD]}
    local IFS=$' \t\n' WORDS
    local SED_COM='sed -En '\''/^Available (sub)?commands:/,${ s/^  ([^[:blank:]]+).*/\1/p }'\'
    local SED_OPT='sed -En -e '\''s/^... (--[^[:blank:]<]+).*/\1/; tX; b'\'' -e '\'':X s/\[|\]//g; p; tY; b'\'' -e '\'':Y s/no-//p'\'

    # ${COMP_LINE} 변수의 경우 현재 입력중인 미완성 단어가 그대로 값으로 전달되므로
    # --help 옵션 적용시 오류가 발생하지 않게 ${COMP_LINE% *} 를 이용해 제거해줍니다.
    if [ "${CUR:0:1}" = "-" ]; then
        if WORDS=$( eval "${COMP_LINE% *} --help" 2>&1 ); then
            WORDS=$( echo "$WORDS" | eval "$SED_OPT" )
        else
            echo; echo "$WORDS" | head -n1 >&2     # 오류 발생시 메시지 출력
            return
        fi
    else
        if [ "${COMP_CWORD}" -eq 1 ]; then
            WORDS=$( $COM --help | eval "$SED_COM" )
        else
            if WORDS=$( eval "${COMP_LINE% *} --help" 2>&1 ); then
                WORDS=$( echo "$WORDS" | eval "$SED_COM" )
            else
                echo; echo "$WORDS" | head -n1 >&2
                return
            fi
        fi
    fi
    COMPREPLY=( $(compgen -W "$WORDS" -- "$CUR") )
    [ "${COMPREPLY: -1}" = "=" ] && compopt -o nospace
}

complete -o default -F _dart dart

6.

[ Virtualbox 자동 완성 ]

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

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

Completion 활용

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

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

시스템의 completion 스크립트 파일 위치

bash shell 은 시작시 자동으로 ~/.bash_completion 파일과 /usr/share/bash-completion/ , /etc/bash_completion.d , $HOME/.local/share/bash-completion/completions/(*.bash 확장자) 디렉토리에서 스크립트 파일을 읽어들입니다.

Quiz

현재 디렉토리에서 파일은 제외하고 디렉토리만 출력하려면 어떻게 할까요?

# 현재 디렉토리 구성이 다음과 같을 경우
$ ls -a
./  ../  .aaa/  .bbb/  .ccc/  bar/  foo/  zoo/  .hidden.txt  file1.txt  file2.txt


$ compgen -d                         $ compgen -d $PWD/      # 전체 경로를 출력  
.aaa                                 /home/mug896/tmp/.aaa
.bbb                                 /home/mug896/tmp/.bbb
.ccc                                 /home/mug896/tmp/.ccc
bar                                  /home/mug896/tmp/bar
foo                                  /home/mug896/tmp/foo
zoo                                  /home/mug896/tmp/zoo

이번에는 모든 파일을 출력

$ compgen -f                         $ compgen -f $PWD/      # 전체 경로를 출력
.aaa                                 /home/mug896/tmp/.aaa
.bbb                                 /home/mug896/tmp/.bbb
.ccc                                 /home/mug896/tmp/.ccc
.hidden.txt                          /home/mug896/tmp/.hidden.txt
bar                                  /home/mug896/tmp/bar
file1.txt                            /home/mug896/tmp/file1.txt
file2.txt                            /home/mug896/tmp/file2.txt
foo                                  /home/mug896/tmp/foo
zoo                                  /home/mug896/tmp/zoo

디렉토리를 제외하고 파일만 출력하려면 다음과 같이 하면 됩니다.

$ find -maxdepth 1 ! -type d 
./file1.txt
./file2.txt
./.hidden.txt

$ find * .*[^.]* -maxdepth 1 ! -type d         # './' 를 제거하고 출력
file1.txt
file2.txt
.hidden.txt

$ find -maxdepth 1 ! -type d ! -name '.*'      # hidden 파일은 제외하고 출력
./file1.txt
./file2.txt

$ find * -maxdepth 1 ! -type d 
file1.txt
file2.txt