Accessing bash completions for specific commands programmatically - bash

I'm trying to write a small command launcher application, and would like to use bash's tab completions in my own completion system. I've been able to get a list of completions for general commands using compgen -abck.
However, I would also like to get completions for specific commands: for instance, the input git p should display completion for git's commands.
Is there any way I can use compgen to do this? If not, are there any other ways I can get a list of completions programmatically?
[EDIT: To clarify, I'm not trying to provide completion to bash - my app is a GUI command launcher. I'd simply like to use bash's existing completions in my own app.]

I don't really know how it works, but the awesome window manager uses the following Lua code for getting access to bash completion's result:
https://github.com/awesomeWM/awesome/blob/master/lib/awful/completion.lua#L119
Via complete -p we find complete -o bashdefault -o default -o nospace -F _git git. We remember "_git" for later.
The length of "git l" is 5, so we set COMP_COUNT=6. We are completing the first argument to "git", so COMP_CWORD=1.
All together we use the following script:
__print_completions() {
printf '%s\n' "${COMPREPLY[#]}"
}
# load bash-completion functions
source /etc/bash_completion
# load git's completion function
_completion_loader git
COMP_WORDS=(git l)
COMP_LINE='git l'
COMP_POINT=6
COMP_CWORD=1
_git
__print_completions
Output: "log"

Check in the /etc/bash_completion.d/ directory. This is where the different command completion scripts stay.

Quite an old question, but in the mean time I've implemented a script that handles this to reuse completions with ZSH

Here a simple but working example in bash :
function foo_completion()
{
local currentWord=${COMP_WORDS[COMP_CWORD]}
local completionList=""
case "${COMP_CWORD}" in
"1")
completionList="command1 command2 command3";
;;
"2")
completionList="param1 param2 param3";
;;
esac
COMPREPLY=( $( compgen -W "${completionList}" -- ${currentWord} ) )
}
complete -F foo_completion foo
With this kind of code, you will get commandN completed when you type "foo c" + tab and paramN completed when you type "foo command1 p" + tab
You can compute the completion list from the command help.
my2c

Related

using bash_completion with cli tool

I have a custom cli tool that i want to setup with bash_completion but want [tab][tab] to perform an enter action on cmdline.
my bash_completion file for wonder is:
_wonder()
{
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "audit nodes tools create debug delete update" -- $cur) )
}
complete -F _wonder wonder
currently:
$ wonder [tab] [tab]
returns:
ip-10-99-18-249:loco_dsl jasonparmar$ wonder
audit create debug delete nodes tools update
What I want is when i use the tools option from wonder:
$ wonder tools [tab] [tab]
I want the [tab][tab] to force an enter on the cmdline
How can i edit my bash_completion file for wonder to achieve this.
Is this even possible with bash_completion.
Thanks in advance.
If you don't mind using external tools to simulate keyboard input (such as xdotool), try adding this as the first line of your completion function:
(( COMP_CWORD > 1 )) && xdotool key Return
As noted by #RandomUser, completion executing a command is unexpected behavior. See, for instance: echo oops; wonder audit <tab><tab> and imagine rm -rf * instead of echo. Consider simply stopping the completion to indicate wonder accepts only one argument:
(( COMP_CWORD > 1 )) && return

autocomplete boost program options

after hours of search I ended up writing my first stackoverflow question.
I want to create a completion function for a bash script, so far so good ;).
This bash script calls other executables that have their own autocompletion.
Example:
$ my_script foo par
# calls /usr/local/libexec/my_script/foo par
Autocompleting the first parameter of my_script (in this case "foo") works, because the possible options are the files in the folder "/usr/local/libexec/my_script/".
Each program in this folder does have a working auto completion, which was a byproduct of using boost::program_options.
I now want to implement the auto completion for the next parameters of my_script by referencing to the auto completion of the program gooing to be called.
$ my_script foo <tab>
# should output possible options to the foo subcommand
# like /usr/local/libexec/my_script/foo <tab>
I've started by this answer Bash completion from another completion, but _command or _command_offset 1 does not seem to work for me.
How can I get the options of foo, and how can I use this in my_script?
My current /etc/bash_completion.d/my_script looks like the following
_my_script()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$COMP_CWORD" == 1 ]]; then
# 1. param: for program to be loaded
for i in $( ls /usr/local/libexec/my_script/ ); do
opts="${opts} ${i} "
done
COMPREPLY=( $(compgen -W "${opts}" ${cur}) )
else
# next param: of the program to be loaded
# how do I get the options of "foo" here?
fi
return 0
}
complete -F _my_script my_script
As soon as I read your question, the completion of sudo and git came to my mind. They both have the similar behavior you desired. So I looked for their completion functions. Here are your missing lines:
local root_command=${COMP_WORDS[0]}
_command_offset 1
It's copied from /usr/share/bash-completion/completions/sudo in Ubuntu 16.04. I totally don't knows its meaning. But it works.

Bash autocompletion for pake WITH local files

I am using bash autocompletion for pake
_pake()
{
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "$(/www/grepo/vendor/bin/pake tasklist)" -- $cur) )
}
complete -F _pake pake
That works fine for all pake tasks, but how do I now add completion for my file system in addition to the task list?
Got a reply to that question as a message. The solution is quite easy, you can add a fallback behaviour to complete
complete -o default -F _pake pake
Given that, the auto complete behaviour falls bask to shell default if the custom completion does not deliver results

Arbitrary command completion - possible?

I'm looking for a way to hook in a custom bash completion function. Problem is, I want this completion function not just for a specific command, but for all commands.
Is this even possible? Having looked around for a while, I couldn't find any resources online.
To reduce the problem to the most trivial case: would it be possible to always have tab-completion for the string 'foo'?
Meaning echo f<tab> would expand into echo foo, and ls fo<tab> would expand into ls foo
For context: I'm trying to implement something similar to http://blog.plenz.com/2012-01/zsh-complete-words-from-tmux-pane.html in bash, but I'm starting to fear it's not possible.
You can do that with the -D option of the complete command:
suggest_hello()
{
COMPREPLY=( hello )
return 0
}
complete -D -F suggest_hello
Now whenever I type echo h<Tab>, I get echo hello.
$ help complete
complete: ...
...
-D apply the completions and actions as the default for commands
without any specific completion defined
...

What is the bash variable $COMP_LINE and what does it do?

What is the $COMP_LINE variable in bash scripting? The Bash Reference Manual has the following to say.
$COMP_LINE
The current command line. This variable is available only in shell functions and external commands invoked by the programmable completion facilities (see Programmable Completion).
I don't understand what 'the current command line' means.
I am trying to pick apart this script: to see how it manages to intercept bash commands.
hook() {
echo "$#"
}
invoke_hook() {
[ -n "$COMP_LINE" ] && return
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
local command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
hook "$command"
}
trap 'invoke_hook' DEBUG
I am running into trouble figuring out what the following line is supposed to do.
[ -n "$COMP_LINE" ] && return
I assume it some sort of check or test before you run the rest of the script, since [] is an alias for the bash test command, but since I can't read it I can't figure out what it's supposed to be testing.
In Bash, if you have a file myfile.txt, you can edit it with nano myfiTab. This completes the filename automatically to save you typing, turning the command into nano myfile.txt. This is known as filename completion.
However, not all commands accept filenames. You may want to be able to do ssh myhoTab and have it complete to ssh myhostname.example.com.
Since bash can't possibly be expected to maintain this logic for all known and unknown commands across all systems, it has programmable completion.
With programmable completion, you can define a shell function to call that will get all hostnames from .ssh/known_hosts and make them available as completion entries.
When this function is invoked, it can examine the variable $COMP_LINE to see the command line it should give suggestions for. If you have set up complete -F myfunction ssh and type ssh myhoTab, then myfunction will run and $COMP_LINE will be set to ssh myho.
This functionality is used by your snippet to make the interceptor ignore commands run as a result of pressing Tab. Here it is with comments:
# This is a debug hook which will run before every single command executed
# by the shell. This includes the user's command, but also prompt commands,
# completion handlers, signal handlers and others.
invoke_hook() {
# If this command is run because of tab completion, ignore it
[ -n "$COMP_LINE" ] && return
# If the command is run to set up the prompt or window title, ignore it
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
# Get the last command from the shell history
local command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
# Run the hook with that command
hook "$command"
}

Resources