在 中,使用内置命令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 调用)。
这种方法可以很好地与仅用于自定义命令完成的不同键绑定配合使用,但它根本无法实现此后的完整完成逻辑(例如命令行中的后续单词)。这样做需要解析命令行、处理光标位置以及其他可能不应该在 shell 脚本中考虑的棘手事情...
答案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
。