Bash 명령 완료를 사용자 정의하는 방법은 무엇입니까?


24

bash에서는 complete내장 명령을 사용하여 명령 인수의 사용자 정의 완료를 쉽게 설정할 수 있습니다 . 예를 들어, 시놉시스가있는 가상 명령의 foo --a | --b | --c경우 다음을 수행 할 수 있습니다.complete -W '--a --b --c' foo

당신은 또한 당신이 누를 때 당신이 얻을 완료 사용자 정의 할 수 있습니다 Tab에서 사용하여 프롬프트 complete -E, 예를 complete -E -W 'foo bar'. 그런 다음 빈 프롬프트에서 tab을 누르면 fooand 만 제안됩니다 bar.

비어 있지 않은 프롬프트 에서 명령 완료를 어떻게 사용자 정의 합니까? 예를 들어 내가 앉아있는 경우 :

anthony@Zia:~$ f

완료를 사용자 정의하여 탭을 누르면 항상 다음과 같이 완료됩니다 foo.

(내가 원하는 실제 사례는 locTABlocalc입니다. 그리고 저에게 물어 보라고했던 오빠는 mplayer로 원합니다).


2
단순히 에일리어싱 loc을 고려 했습니까 localc? 꽤 오랜 시간 파고 검색 한 후에 bash 완성을이 방법으로 사용자 정의하는 방법을 찾지 못했기 때문에 대안을 제안합니다. 가능하지 않을 수 있습니다.
jw013

@ jw013 예, 잠시 찾았지만 아무것도 찾을 수 없었습니다. 나는 누군가가 zsh로 전환 할 것을 제안하기를 기대하고 있습니다. 적어도 zsh가 그렇게한다면, 다음 버전의 bash도 편안하게 알 수 있습니다. 😀
derobert

Tab 키를 두 번 눌러야 명령이 완료됩니다.
Floyd

1
그래도 다른 모든 완성품이 깨지지 않습니까? 나는 그것이 가능하다하더라도, 당신은 더 이상 얻을 수 없을 것, 의미 locate, locale, lockfile또는 다른 확장의 loc. 아마도 더 나은 접근 방식은 특정 키에 다른 키를 매핑하는 것입니다.
terdon

1
그러한 맥락의 예를 제시 할 수 있습니까 (원하는 효과는 loc<TAB>->localc).
damienfrancois 2014

답변:


9

명령 완료 (다른 것들과 함께)는 bash readline complete를 통해 처리됩니다 . 이것은 일반적인 "프로그래밍 가능 완료"보다 약간 낮은 수준에서 작동합니다 (명령이 식별 될 때만 호출되며 위에서 식별 한 두 가지 특수한 경우).

업데이트 : bash-5.0 (2019 년 1 월)의 새로운 릴리스는 complete -I이 문제를 정확하게 추가합니다 .

관련 readline 명령은 다음과 같습니다.

complete (TAB)
       Attempt to perform completion on the text  before  point.   Bash
       attempts completion treating the text as a variable (if the text
       begins with $), username (if the text begins with  ~),  hostname
       (if  the  text begins with @), or command (including aliases and
       functions) in turn.  If none of these produces a match, filename
       completion is attempted.

complete-command (M-!)
       Attempt  completion  on  the text before point, treating it as a
       command name.  Command completion attempts  to  match  the  text
       against   aliases,   reserved   words,  shell  functions,  shell
       builtins, and finally executable filenames, in that order.

보다 일반적인 방법과 비슷하게이 complete -F중 일부를 사용하여 함수로 넘길 수 있습니다 bind -x.

function _complete0 () {
    local -a _cmds
    local -A _seen
    local _path=$PATH _ii _xx _cc _cmd _short
    local _aa=( ${READLINE_LINE} )

    if [[ -f ~/.complete.d/"${_aa[0]}" && -x  ~/.complete.d/"${_aa[0]}" ]]; then
        ## user-provided hook
        _cmds=( $( ~/.complete.d/"${_aa[0]}" ) )
    elif [[ -x  ~/.complete.d/DEFAULT ]]; then
        _cmds=( $( ~/.complete.d/DEFAULT ) )
    else 
        ## compgen -c for default "command" complete 
        _cmds=( $(PATH=$_path compgen -o bashdefault -o default -c ${_aa[0]}) )  
    fi

    ## remove duplicates, cache shortest name
    _short="${_cmds[0]}"
    _cc=${#_cmds[*]} # NB removing indexes inside loop
    for (( _ii=0 ; _ii<$_cc ; _ii++ )); do
        _cmd=${_cmds[$_ii]}
        [[ -n "${_seen[$_cmd]}" ]] && unset _cmds[$_ii]
        _seen[$_cmd]+=1
        (( ${#_short} > ${#_cmd} )) && _short="$_cmd"
    done
    _cmds=( "${_cmds[@]}" )  ## recompute contiguous index

    ## find common prefix
    declare -a _prefix=()
    for (( _xx=0; _xx<${#_short}; _xx++ )); do
        _prev=${_cmds[0]}
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
             [[ "${_cmd:$_xx:1}" != "${_prev:$_xx:1}" ]] && break
            _prev=$_cmd
        done
        [[ $_ii -eq ${#_cmds[*]} ]] && _prefix[$_xx]="${_cmd:$_xx:1}"
    done
    printf -v _short "%s" "${_prefix[@]}"  # flatten 

    ## emulate completion list of matches
    if [[ ${#_cmds[*]} -gt 1 ]]; then
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
            [[ -n "${_seen[$_cmds]}" ]] && printf "%-12s " "$_cmd" 
        done | sort | fmt -w $((COLUMNS-8)) | column -tx
        # fill in shortest match (prefix)
        printf -v READLINE_LINE "%s" "$_short"
        READLINE_POINT=${#READLINE_LINE}  
    fi
    ## exactly one match
    if [[ ${#_cmds[*]} -eq 1 ]]; then
        _aa[0]="${_cmds[0]}"
        printf -v READLINE_LINE "%s " "${_aa[@]}"
        READLINE_POINT=${#READLINE_LINE}  
    else
        : # nop
    fi
}

bind -x '"\C-i":_complete0'

이를 통해 고유 한 명령 또는 접두사 문자열 후크를 사용할 수 ~/.complete.d/있습니다. 예를 들어 다음을 사용하여 실행 파일을 만드는 경우 ~/.complete.d/loc:

#!/bin/bash
echo localc

이것은 당신이 기대하는 것을 (거의) 할 것입니다.

위의 함수는 불완전하지만 (특히 sort | fmt | column일치 목록을 표시하는 모호한 캐리 온) 정상적인 bash 명령 완료 동작을 에뮬레이트하기 위해 약간의 길이로 진행됩니다 .

그러나 이것 에 대한 사소한 문제는 함수를 사용하여 기본 complete기능 (기본적으로 TAB으로 호출)에 대한 바인딩을 대체 할 수 있습니다 .

이 접근 방식은 사용자 지정 명령 완성에만 사용되는 다른 키 바인딩과 잘 작동하지만 그 이후에는 전체 완성 논리를 구현하지 않습니다 (예 : 명령 줄의 이후 단어). 그렇게하려면 명령 줄을 구문 분석하고 커서 위치를 다루고 쉘 스크립트에서 고려해야 할 까다로운 까다로운 것들이 필요합니다 ...


1

나는이에 대한 필요성을 unterstood 경우 몰라요 ...
이것은 당신의 bash는 단지 하나의 명령 시작을 알고 있다는 것을 의미하는 것이다 f.
완성의 기본 아이디어는 다음과 같습니다. 모호한 경우 가능성을 인쇄하십시오.
따라서이 PATH명령을 포함하는 디렉토리로 설정하고 모든 bash 내장을 비활성화 하여이 작업을 수행 할 수 있습니다.

어쨌든, 나는 또한 당신에게 일종의 해결책을 줄 수 있습니다.

alias _='true &&'
complete -W foo _

따라서 입력 _ <Tab>하면 _ foo실행 이 완료됩니다 foo.

그러나 그럼에도 불구하고 alias f='foo'훨씬 쉬울 것입니다.


0

당신을위한 간단한 대답은

$ cd into /etc/bash_completion.d
$ ls

기본 출력 만

autoconf       gpg2               ntpdate           shadow
automake       gzip               open-iscsi        smartctl
bash-builtins  iconv              openssl           sqlite3
bind-utils     iftop              perl              ssh
brctl          ifupdown           pkg-config        strace
bzip2          info               pm-utils          subscription-manager
chkconfig      ipmitool           postfix           tar
configure      iproute2           procps            tcpdump
coreutils      iptables           python            util-linux
cpio           lsof               quota-tools       wireless-tools
crontab        lvm                redefine_filedir  xmllint
cryptsetup     lzma               rfkill            xmlwf
dd             make               rpm               xz
dhclient       man                rsync             yum.bash
e2fsprogs      mdadm              scl.bash          yum-utils.bash
findutils      module-init-tools  service
getent         net-tools          sh

원하는 프로그램을 추가하여 자동 완성을 bash 완료


2
그 파일에는 쉘 스크립트가 있으며 올바르게 기억하면 상당히 복잡합니다. 또한 명령을 이미 입력 한 후에 만 ​​인수를 완료합니다 ... 명령을 완료하지 않습니다.
derobert

네가 무슨 말을 하려는지 알 겠어. 이 문서를 볼 수있을 때 : debian-administration.org/article/316/…
unixmiah

-3

아래 명령을 실행하여 mplayer 바이너리가 설치된 위치를 찾으십시오.

which mplayer

또는 mplayer 바이너리에 대한 경로를 알고 있다면 아래 명령에서 사용하십시오.

ln -s /path/to/mplayer /bin/mplayer

$PATH변수에 지정된 모든 디렉토리에서 입력하는 것이 이상적 입니다.


2
Mathew 안녕하세요. Unix & Linux에 오신 것을 환영합니다. OP의 질문은 귀하가 제안하는 답변보다 조금 더 복잡하다고 생각합니다. 나는 mplayer이미 그들에 있다고 추정 $PATH하고 그들은 (예를 들어, 등으로 시작하는) 모든 바이너리 대신에, 무언가 mp<tab>를 생산 하고 싶어한다mplayermpmpage mpcd mpartition mplayer
drs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.