![Bash 명령 완성을 사용자 정의하는 방법은 무엇입니까?](https://linux55.com/image/41136/Bash%20%EB%AA%85%EB%A0%B9%20%EC%99%84%EC%84%B1%EC%9D%84%20%EC%82%AC%EC%9A%A9%EC%9E%90%20%EC%A0%95%EC%9D%98%ED%95%98%EB%8A%94%20%EB%B0%A9%EB%B2%95%EC%9D%80%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
에서는 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
?
(내가 실제로 원하는 것은 loc
TAB→ 입니다 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>
완료됩니다 ._ foo
foo
하지만 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
.