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

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

에서는 bash내장된 명령을 사용하여 명령 매개변수 설정의 사용자 정의 완료가 매우 쉽습니다. complete예를 들어 가상 명령의 경우 요약은 다음과 같습니다.

foo --a | --b | --c

넌 할 수있어

complete -W '--a --b --c' foo

Tab을 누를 때 나타나는 완료 결과를 사용자 정의할 수도 있습니다.비어 있는complete -E예를 들어 complete -E -W 'foo bar'. 그런 다음 빈 프롬프트에서 Tab 키를 누르면 foo및 만 제안됩니다 bar.

명령 완성을 사용자 정의하는 방법아니요- 메시지가 비어 있나요? 예를 들어 이라고 쓰면 f로 완료되도록 완성을 어떻게 사용자 정의할 수 있나요 foo?

(내가 실제로 원하는 것은 locTAB→ 입니다 localc. 형이 나에게 이 질문을 하라고 권유했는데, 그는 그것을 원합니다 mplayer.)

답변1

무엇보다도 명령 완료는 bash를 통해 처리됩니다.줄 완성 읽기. 이는 일반적인 "프로그래밍 가능 완성"(명령이 인식되고 위에서 식별된 두 가지 특수 사례에만 호출됨)보다 약간 낮은 수준에서 실행됩니다.

고쳐 쓰다: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

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

위 함수는 완벽하지는 않지만 정상적인 bash 명령 완료 동작을 어느 정도 시뮬레이션합니다(특히 sort | fmt | column일치 항목 목록을 표시하는 의심스러운 이월).

하지만complete이는 큰 문제인데, 메인 함수에 대한 바인딩을 대체하기 위해 함수만 사용할 수 있습니다 (기본적으로 TAB 호출 사용).

이 접근 방식은 사용자 정의 명령 완성을 위한 다양한 키 바인딩과 잘 작동하지만 그 이후의 전체 완료 논리(예: 명령줄의 후속 단어)는 허용하지 않습니다. 그렇게 하려면 명령줄 구문 분석, 커서 위치 처리 및 쉘 스크립트에서 고려해서는 안되는 기타 까다로운 작업이 필요합니다.

답변2

이에 대한 귀하의 필요성을 이해하는지 모르겠습니다 ...
이는 귀하의 bash가 로 끝나는 것만 알고 있다는 것을 의미합니다 f.
기본 아이디어는 불분명한 경우 가능성을 인쇄하는 것입니다.
따라서 PATH이 명령만 포함하는 디렉터리로 디렉터리를 설정하고 해당 작업을 수행하기 위해 모든 bash 내장 기능을 비활성화할 수 있습니다.

어쨌든 해결책을 드릴 수도 있습니다.

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

그래서 입력하면 실행이 _ <Tab>완료됩니다 ._ foofoo

하지만 alias f='foo'어쨌든 훨씬 쉬울 것입니다.

답변3

당신을 위한 간단한 대답은

$ 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 완료를 자동화하려는 프로그램을 추가하기만 하면 됩니다.

답변4

mplayer 바이너리가 설치된 위치를 찾으려면 다음 명령을 실행하십시오.

which mplayer

또는 다음 명령에서 mplayer 바이너리 경로를 사용합니다(이미 알고 있는 경우).

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

이상적으로는 입력하는 모든 내용이 변수에 지정된 모든 디렉터리에서 검색됩니다 $PATH.

관련 정보