29

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 locTAB β†’ localc. And my brother, who prompted me to ask this, wants it with mplayer.)

AdminBee
23.6k25 gold badges54 silver badges77 bronze badges
asked Aug 5, 2014 at 12:29
6
  • 2
    Have you considered simply aliasing loc to localc? 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. Commented Aug 5, 2014 at 20:47
  • @jw013 Yeah, I looked for a while and couldn't find anything either. I'm half expecting someone to suggest switching to zsh, too. At least if zsh does it, I can rest comfortably knowing the next version of bash will as well. πŸ˜€ Commented Aug 5, 2014 at 20:51
  • you have to press TAB twice and it will complete commands. Commented Oct 10, 2014 at 15:30
  • 1
    Won't that break all the other completions though? I mean, even if it is possible, you would no longer be able to get locate, locale, lockfile or any of the other expansions of loc. Perhaps a better approach would be to map a different key to that specific completion. Commented Oct 28, 2014 at 18:00
  • 1
    can you give an example of such context (that would lead to the desired effect to be loc<TAB>->localc )? Commented Nov 4, 2014 at 14:29

4 Answers 4

11
+200

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...

answered Jul 23, 2015 at 16:36
1

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.

answered Aug 8, 2014 at 10:41
1

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

Anthon
81.4k42 gold badges174 silver badges228 bronze badges
answered Sep 25, 2014 at 3:33
2
  • 2
    Those 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. Commented 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/… Commented Sep 25, 2014 at 3:53
-3

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.

answered Oct 15, 2014 at 14:21
1
  • 2
    Hi, 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 like mp<tab> to produce mplayer, instead of all the binaries that begin with mp (e.g., mpage mpcd mpartition mplayer etc.) Commented Oct 15, 2014 at 14:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.