Bash completion for command line arguments and options - bash

I'm writing a CLI for my app in go and I would like to provide users with bash completions for options and arguments.
The API is the following myapp [-f file] argument andI would like to provide completion for both the option -f and the argument.
I wonder how I can distinguish between the following two scenarios:
myapp -f some_path<tab><tab>
myapp -f some_file <tab><tab>
In the first case the user is still typing the option and he/she would like to see candidates for the file option. In the second he/she finished typing and would like to see the candidates for the argument.
The problem is that in both cases the value of os.Args is identical and I can't distinguish between the two scenarios. Is there a way to access the invocation string?
One solution would be to use a separator like -- between the option and the argument but I would like to know if there is a cleaner solution before I go down this path.

Related

Am I able to deactivate verbose output in the bash?

I have aliases for many commands with their verbose flags, e.g.:
alias ninja='ninja --verbose -j 0'
Is there a mechanism in bash, where I can deactivate this flag afterwards?
I tried stuff like:
ninja --verbose=0
but that didn't work out.
I know that I can hide my output with /dev/null or that I can execute the binary directly with /path/to/ninja, but that's not the intent of my question. The answer might be command specific and depends on which mechanism for passing parameters the appropriate program uses, e.g. getopts. Anyways, I am looking forward to your help.
EDIT:
From comments I learned, that command ninja or escaping like nin\ja will ignore the complete alias, but not a specific parameter.

Bash completion using output from script

I know there are a lot of tutorials on bash completion, but I just can't figure this out.
All I want is this. If I type myscript[tab][tab], then "myscript list-commands" is run. It will output a space delimited list of available commands (but I can output it however is appropriate). That output list is used for tab completion.
What do I put it my .bashrc to make that happen?
The easiest way is using a list of words/commands that your script supports:
Put the following into your .bashrc to have your script myscript support the commands add, list, delete:
complete -W "add list delete" myscript
This will leads to
> myscript [tab][tab]
add list delete
Hope this help. For further, more dynamic options than a simple wordlist have a look at the manpage of the complete command: https://ss64.com/osx/complete.html

What is the standard usage argument style?

I'm making some command-line tools for some research I'm doing. I'd like these tools to follow commonly used conventions regarding command line programs in Unix.
Should I use flags or just list parameters?
program one two three
program -a one -b two -c three
Where in the list of commands does the input file normally go, or is it better to < it into the program?
What about the output filename?
Should I specify the file extension for the output format, or have my program automatically put the correct extension on?
When the user enters an invalid command, is there a prototypical "correct usage" message?
Is "--help" or "-h" required?
Also, is there some sort of header file I can include that would help with managing these?
If you're looking for a "standard", then you could do worse than look at GNU's Standards for Command Line Interfaces. Other standards are available.
As far as coding for this goes, take a look at boost::program_options. Not only will this save you rolling a lot of your own code, but it does a good job of formatting the options for presenting to the user (the prototypical "correct usage" message, you asked for).
In answer to your specific questions:
Where in the list of commands does the input file normally go, or is it better to < it into the program?
I would expect these to come at the end of a command line. Like in GNU grep. If you are only processing one file and would like to make stdin available as an input source, that would not surprise most users.
If your command processes lots of files, then it would be unusual to have to specify a switch before the filenames. Think cat.
What about the output filename?
A -o or --output option is fairly common. If your file takes exactly one input and one output, then program inputfile outputfile would not surprise many users. If no output file is specified, perhaps you'll output to stdout; that would not be unusual behaviour and would allow your users to pipe the output through other commands (such as grep, less, etc...), They could also redirect stdout to a file using >.
Should I specify the file extension for the output format, or have my program automatically put the correct extension on?
This is probably a matter for debate. If I specified an output filename, I would expect to find that file created (or replaced, after a prompt) without the program changing the name.
When the user enters an invalid command, is there a prototypical "correct usage" message?
Using GNU grep as an example again:
grep: unrecognized option '--incorrect'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
This wouldn't surprise too many users and points them in the right direction if they've made a typo without swamping them with information.
Is "--help" or "-h" required?
That depends on your customer! I find it frustrating when this option isn't available.
Usually speaking, flags are there for providing options and parameter are for passing information. If you have input,output file as command line argument, use flags like -i -o, so sequence will not matter. -h is required if you want to (and need to) give documentation.

Can bash completion be invoked programmatically?

What I want is a function I can call from a program so it completes the way bash would given a commandline and a location where TAB was pressed.
. /etc/bash_completion
generate_completions "command arg1 arg2" 17
would return the same thing as
command arg1 arg2[TAB]
I haven't seen any way to do this.
I actually had to do this to figure out how apt-get autocomplete works on ubuntu (built my own pseudo-repository tool :)
This is a multistep process:
First, complete -p will give you a listing of all completions in the form of a set of commands you can run to replicate the configuration. For example, lets say you want to hunt down the autocomplete for apt-get. Then:
$ complete -p | grep apt-get
complete -F _apt_get apt-get
This tells you that the shell function _apt_get is called by the completion mechanism.
You need to recreate the special variables used by the function,, namely COMP_LINE (the full line), COMP_WORDS (bash array of all of the arguments -- basically split COMP_LINE), COMP_CWORD (index, should point to last value), COMP_POINT (where within the word you are doing the autocomplete), and COMP_TYPE (this is how you tell it that you want to complete as if you hit tab).
Note: read the manpage for more info -- this is how i figured it out in the first place. man bash

bash script: How to implement your own history mechanism?

I'm implementing an interactive bash script similar to the MySQL client, /usr/bin/mysql. In this script I need to issue various types of 'commands'. I also need to provide a history mechanism whereby the user can use the up/down arrow keys to scroll through the commands entered so far.
The snippet listed here (Example 15-6, Detecting the arrow keys) does not exactly do what I want it to. I really want the following:
The up/down arrow keys should operate in silent mode. Meaning, they should not echo their character codes on the terminal.
The other keys however (which will be used to read the command names and their arguments) must not operate in silent mode.
The problem with read -s -n3 is that it does not satisfy my simultaneously conflicting requirements of silent mode and echo mode, based solely on the character code. Also, the value -n3 will work for arrow keys but, for other/regular keys, it won't 'return control' to the calling program until 3 characters have been consumed.
Now, I could try -n1 and manually assemble the input, one character at a time (yuck!). But the character-code based silent-/echo-mode switching problem would still persist!
Has anyone attempted this thing in bash? (Note: I cannot use C, nor other scripting languages like Perl, Python, etc.)
EDIT
Continuing with Dennis' answer... You will also need to manually add your desired entries to your history via history -s, like so...
while read -e x; do
history -s "$x"
# ...
done
You can use read -e to have read use readline. It will process your cursor keys and maintain the history for you. You will also need to manually add your desired entries to your history via history -s, like so:
while read -e x; do
history -s "$x"
# ...
done
MySQL and Bash use the Readline library to implement this. Maybe you could use something like rlwrap or rlfe?
rlwrap has a special "one-shot" mode to act as a replacement for the 'read' shell command. If you wish, every occurrence of this command in your script can be given its own history and completion word list.
Use it like this:
REPLY=$(rlwrap -o cat)
or, specifying a history file and a completion wordlist:
REPLY=$(rlwrap -H my_history -f my_completions -o cat)

Resources