bashshelltab-completion

Bash: Getting a command's completion output programmatically (e.g., in a variable)


How to tap into the completion of another command programmatically?

Supposing my current directory has files a1, a2, and a3, then how can I make my command invoke the autocompletion of ls a to get back a1 a2 a3?

Is this possible?


Clarification and justification:

I chose ls because people can relate to it. It is a contrived example that is intentionally simple so that people can understand the question without distractions, but unfortunately such examples sometimes take us on tangents. :)

Let me try to exemplify the value of this feature. I have a command called build which, given a directory, can autocomplete to the targets that can be built in that directory. Those targets may not correspond to the files from that directory, and so glob completion (* and other wildcard characters) will not work. The targets might be mined by the build command from a build file that I don't want to be parsing. In other words:

build path/to/dir/TABTAB

Might give:

path/to/dir/a_target
path/to/dir/b_target

Again, a_target and b_target are not files or directories.

build is a pre-existing command, not something I can go ahead and modify to suit my purposes. And the manner in which it comes up with the valid completions is something I certainly don't want to know or reinvent.

Now suppose I have an entire repository of buildable projects, and most of my work and therefore most of my build work happens in only one project. In other words, I always build targets under my/project/directory.

So far so good.

I want to write a wrapper around the build command that doesn't require me to feed it the directory path each time I run it. I want it to know my preferred project directory (or directories, why not) and let me reference the targets without qualifying them:

So under the assumption that I have:

my/project/directory/a_target
my/project/directory/b_target

I want this:

mybuild TABTAB

to give me:

a_target
b_target

Basically I want to decorate and simplify the behavior of build to suit my particular needs.

I will need to write my own completion code for mybuild, but I want it to rely on the completion for build, because I can't ask the developers of build to code a build listtargets command just to make me happy. (Although that would be much more robust.) The build command already has code somewhere that given a prefix can return all the matching targets. It's in the completion for build, and I need to tap into it.

(Of course, when I run mybuild a_target, I will make sure that it knows to run build my/project/directory/a_target. I know how to implement this and it is not in scope for this question.)

I hope this illustrates why I need to tap into the completion of the build command and invoke it as a black box.


Solution

  • This is a bit of an odd thing to do, and the command you need to execute depends on the number of files in the directory - none, one, or more than one. But this command works for the example case:

    echo echo a$'\t'$'\t' | bash -i 2>&1 | head -3 | tail -1
    

    The command being autocompleted is

    echo a
    

    so send that as a character stream, followed by two tab characters, into an interactive bash shell. bash produces the autocompletion output on stderr, so redirect that to stdout and pipe that through head and tail to select one line of output from the whole. That produces, in this case, the one-line output

    a1  a2  a3 
    

    But, as others say, just using

    echo a*
    

    might be easier!