Custom Bash Autocomplete with File Path Completion - bash

I'm working on bash auto-completion for a project I'm the maintainer of. You can find the script here. I've cobbled this together with some hacking on my own, and with the help of some contributors who understand that completion APIs better than I do.
What we have works great -- with one exception. We can manage a completion like like this
//type
pestle.phar som[TAB]
//completes to
pestle.phar some-command-name
However, once we're here we lose file path/name completion that's a part of the stock bash shell. That is, working off the previous example, if a user types
//type
pestle.phar some-command-name /va[TAB]
we'd like it to complete to
//completes to the following, because var exists
pestle.phar some-command-name /var
Is there a way to just tell the complete command something like
Hey, in addition to everything we're telling you to do with our custom bash function, also keep your normal file path completion
If not, is there there some known science/boilerplate to reimplementing the file path completion in your own custom base completion functions?
Some other answers and the docs seem to indicate that the -o filenames or -o bashdefault options should take care of this -- but it doesn't seem to be working on OS X 10.11. I'm not sure if I misunderstand -o, or if the code in my completion files somehow overrides the -o behavior, or if OS X is doing it's I'm only a mostly well behaved unix thing.
Also -- if it's not obvious -- this is my first deep bash completion rodeo. If I've said something seemingly dumb/naive above please let me know. I may be looking for a fish right now, but I'd like to learn to fish in the bash completion river myself.

I think -o default (without -o filenames) should work for you. According to the manual:
bashdefault
Perform the rest of the default bash completions if the compspec generates no matches.
default
Use readline's default filename completion if the compspec generates no matches.
filenames
Tell readline that the compspec generates filenames, so it can perform any filename-specific processing (like adding a slash to directory
names, quoting special characters, or suppressing trailing spaces). Intended to be used with shell functions.
(Also see 'complete -d -o default cd' issue for the difference between -o default and -o bashdefault.)

Related

How to view commands that `make` executes?

I have a Makefile, which fails at some point, with a git error. How can I view the git command that causes the whole make process to fail? More precisely, I am looking for a list of commands (including the ones that start with #) that I can run on an identical setup, to achieve the same effect as what make does.
I know for a script, instead of #! /bin/bash you would add a flag -x to it, and that would display all the commands before their execution. How do I do the same thing for make?
I am looking for a list of commands (including the ones that start with #) that I can run on an identical setup, to achieve the same effect as what make does.
By default, make echoes all recipe commands it runs, except those prefixed with #. The POSIX specifications for make do not describe a way to override that effect of # (but see below). It is conceivable that your make has an extension for that, but the make implementations you are most likely to be using (GNU make or BSD make, since you seem to assume that your standard shell is bash) do not.
Additionally, in POSIX-conforming make implementations, including the two mentioned above, the special target .SILENT can be used to suppress echoing the commands of some or all targets, and the -s command-line option can be used to suppress echoing for all targets.
You can print recipe commands prefixed with # if you run make with the -n (no-op) flag. That will print the commands for out-of-date targets without running them, except that those prefixed with a + are run even in no-op mode. Commands prefixed with # are included among those printed. Under some circumstances, the fact that most commands are not actually run in this mode can affect the output, but all the cases I can think of at the moment involve recursive make, and I think they are fairly unlikely.
POSIX seems to indicate that -n does not override -s or .SILENT, so if you have to deal with those then you may have no alternative but to modify your makefile. If you happen to be using GNU make, however, you will find that -n does override .SILENT and -s in that implementation. The same may be true of other makes.

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.

Is it possible to have separate bash completion functions for separate commands which happen to share the same name?

I have two separate scripts with the same filename, in different paths, for different projects:
/home/me/projects/alpha/bin/hithere and /home/me/projects/beta/bin/hithere.
Correspondingly, I have two separate bash completion scripts, because the proper completions differ for each of the scripts. In the completion scripts, the "complete" command is run for each completion specifying the full name of the script in question, i.e.
complete -F _alpha_hithere_completion /home/me/projects/alpha/bin/hithere
However, only the most-recently-run script seems to have an effect, regardless of which actual version of hithere is invoked: it seems that bash completion only cares about the filename of the command and disregards path information.
Is there any way to change this behavior so that I can have these two independent scripts with the same name, each with different completion functions?
Please note that I'm not interested in a solution which requires alpha to know about beta, or which would require a third component to know about either of them--that would defeat the purpose in my case.
The Bash manual describes the lookup process for completions:
If the command word is a full pathname, a compspec for the full pathname is searched for first. If no compspec is found for the full pathname, an attempt is made to find a compspec for the portion following the final slash. If those searches do not result in a compspec, any compspec defined with the -D option to complete is used as the default.
So the full path is used by complete, but only if you invoke the command via its full path. As for getting completions to work using just the short name, I think your only option (judging from the spec) is going to be some sort of dynamic hook that determines which completion function to invoke based on the $PWD - I don't see any evidence that Bash supports overloading a completion name like you're envisioning.
Yes, this is possible. But it's a bit tricky. I am using this for a future scripting concept I am developing: All scripts have the same name as they are build scripts, but still bash completion can do its job.
For this I use a two step process: First of all I place a main script in ~/.config/bash_completion.d. This script is designed to cover all scripts of the particular shared script name. I configured ~/.bashrc in order to load that bash completion file for these scripts.
The script will obtain the full file path of the particular script file I want to have bash completion for. From this path I generate an identifier. For this identifier there exists a file that provides actual bash completion data. So if bash completion is performed the bash completion function from the main bash completion script will check for that file and load it's content. Then it will continue with regular bash completion operation.
If you have two scripts with the same name you will have two different identifiers as those scripts share the same name but have different paths. Therefore two different configurations for bash completion can be used.
This concept works like a charm.
NOTE: I will update this answer soon providing some source code.

Prog Challenge - Find paths to files called from configuration files or scripts

I have no idea how to do that, so I come here for help :) Here is what I'd need. I need to parse some configuration files or bash/sh scripts on a Red Hat Linux system, and look for the paths to the files/commands/scripts meant to be executed by them. The configuration files can have different syntax or be using different languages.
Here are the files I have to look at:
Config scripts:
/etc/inittab
/var/spool/cron/root
/var/spool/cron/tabs/root
/etc/crontab
/etc/xinetd.conf
Files located under /etc/cron.d/* recursively
Bash / Sh scripts:
Files located under /etc/init.d/* or /etc/rc.d/* recursively. These folders contain only shell scripts so maybe all the other files listed above need separate treatment.
Now here's the challenges that I can think of:
The paths within the files may be absolute or relatives ;
The paths within the files may be at the beginning of lines or preceded by a character such as space, colon or semicolon ;
File paths expressed as arguments to commands/scripts must be ignored ;
Paths to directories must be ignored ;
Shell functions or built-in commands must be ignored ;
Some examples (extracted from /etc/init.d/avahi-daemon):
if [ -s /etc/localtime ]; then
cp -fp /etc/localtime /etc/avahi/etc >/dev/null 2>&1
-> Only /bin/cp and /bin/[ must be returned in the snippet above (its the only commands actually executed)
AVAHI_BIN=/usr/sbin/avahi-daemon
$AVAHI_BIN -r
-> /usr/sbin/avahi-daemon must be returned, but only because the variable is called after.
Note that I do not have access to the actual filesystem, I just have a copy of the files to parse.
After writing this up, I realize how complicated it is and unlikely to have a 100% working solution... But if you like programming challenges :)
The good part is I can use any scripting language: bash/sh/grep/sed/awk, php, python, perl, ruby or a combination of these..
I tried to start writing up in PHP but I am struggling to get coherent results.
Thanks!
The language you use to implement this doesn't matter. What matters is that the problem is undecidable, because it is equivalent to the halting problem.
Just as we know that it is impossible to determine if a program will halt, it is impossible to know if a program will call another program. For example, you may think your script will invoke X then Z, but if X never returns, Z will never be invoked. Also, you may not notice that your script invokes Y, because the string Y may be determined dynamically and never actually appear in the program text.
There are other problems which may stymie you along the way, too, such as:
python -c 'import subprocess; subprocess.call("ls")'
Now you need not only a complete parser for Bash, but also for Python. Not to mention solve the halting problem in Python.
In other words, what you want is not possible. To make it feasible you would have to significantly reduce the scope of the problem, e.g. "Find everything starting with /usr/bin or /bin that isn't in a comment". And it's unclear how useful that would be.

Bash alias autocompletion that considers only *.foo files

Whenever I want to start editing my LaTeX sources by launching, e.g., the command vim pdeOptAff.tex, I find that bash the (tab)-autocompletion is still tedious because the folder typically contains a bunch of files with the same name but different endings:
$ ls
pdaeOptAff.aux pdaeOptAff.out pdaeOptAff.tex.latexmain
pdaeOptAff.bbl pdaeOptAff.pdf pdaeOptAff.toc
pdaeOptAff.blg pdaeOptAff.synctex.gz
pdaeOptAff.log pdaeOptAff.tex
Since I always want to open the the ~.tex file, I would love to have a filter that makes autocomplete expand only the ~.tex files.
In addition, I have an alias defined: alias g='gvim --remote-silent'. So this filter, in particular, should work for the alias.
Try:
complete -f -X '!*.tex' g
Then, you can type:
g <TAB>
And get the completions. Note: this doesn't complete directories. Someone else may have a better solution using compgen.
There's a helpful autocomplete guide on The Linux Documentation Project too.

Resources