In bash
, it's easy enough to set up customized completion of command arguments using the complete
built-in. For example, for a hypothetical command with a synopsis of
foo --a | --b | --c
you could do
complete -W '--a --b --c' foo
You can also customize the completion you get when you press Tab at an empty prompt using complete -E
, for example complete -E -W 'foo bar'
. Then, pressing tab at the empty prompt would suggest only foo
and bar
.
How do I customize command completion at a non-empty prompt? For example, if I write f
, how do I customize the completion to make it complete to foo
?
(The actual case I'd like is loc
TAB β localc
. And my brother, who prompted me to ask this, wants it with mplayer
.)
4 Answers 4
Completion of the command (along with other things) is handled via bash readline completion. This operates at a slightly lower level than the usual "programmable completion" (which is invoked only when the command is identified, and the two special cases you identified above).
Update: the new release of bash-5.0 (Jan 2019) adds complete -I
for exactly this problem.
The relevant readline commands are:
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.
In a similar way to the more common complete -F
, some of this can be handed over to a function by using 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'
This enables your own per-command or prefix string hooks in ~/.complete.d/
. E.g. if you create an executable ~/.complete.d/loc
with:
#!/bin/bash
echo localc
This will do (roughly) what you expect.
The function above goes to some lengths to emulate the normal bash command completion behaviour, though it is imperfect (particularly the dubious sort | fmt | column
carry-on to display a list of matches).
However, a non-trivial issue with this it can only use a function to replace the binding to the main complete
function (invoked with TAB by default).
This approach would work well with a different key-binding used for just custom command completion, but it simply does not implement the full completion logic after that (e.g. later words in the command line). Doing so would require parsing the command line, dealing with cursor position, and other tricky things that probably should not be considered in a shell script...
I don't know if I unterstood your need for this...
This would imply that your bash only knows one command beginning with f
.
A basic idea of completion is: if it's ambiguous, print the possiblities.
So you could set your PATH
to a directory only containing this one command and disable all bash builtins to get this work.
Anyhow, I can give you also a kind of workaround:
alias _='true &&'
complete -W foo _
So if you type _ <Tab>
it will complete to _ foo
which executes foo
.
But nethertheless the alias f='foo'
would be much easier.
Simple answer for you would be to
$ cd into /etc/bash_completion.d
$ ls
just the basic outputs
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
just add your desired program to auto complete to bash completion
-
2Those files have shell script in them, some fairly complex if I remember correctly. Also, they only complete arguments once you've already typed the command... They don't complete the command.derobert– derobert2014εΉ΄09ζ25ζ₯ 03:41:22 +00:00Commented Sep 25, 2014 at 3:41
-
I understand what you mean. When you can take a look at this doc: debian-administration.org/article/316/…unixmiah– unixmiah2014εΉ΄09ζ25ζ₯ 03:53:44 +00:00Commented Sep 25, 2014 at 3:53
Run the below command to find where mplayer binary is installed:
which mplayer
OR use the path to the mplayer binary if you aleady know it, in the below command:
ln -s /path/to/mplayer /bin/mplayer
Ideally anything you type is searched in all directories specified in the $PATH
variable.
-
2Hi, Mathew, welcome to Unix & Linux. I think the OP's question is a little more complicated than the answer you're suggesting. I presume
mplayer
is already in their$PATH
and they want something likemp<tab>
to producemplayer
, instead of all the binaries that begin withmp
(e.g.,mpage
mpcd
mpartition
mplayer
etc.)drs– drs2014εΉ΄10ζ15ζ₯ 14:48:37 +00:00Commented Oct 15, 2014 at 14:48
loc
tolocalc
? I suggest alternatives because after quite some time digging and searching I have not found a way to customize bash completion this way. It may not be possible.locate
,locale
,lockfile
or any of the other expansions ofloc
. Perhaps a better approach would be to map a different key to that specific completion.loc<TAB>->localc
)?