Any bash gurus out there? Custom expansion thingies...

I’m having some trouble with my google-fu, probably because I don’t know exactly what to look for. Basically, I’ve got a bunch of directories with names like /foo/bar/baz/quuxNNNN where NNNN is a 4-digit number.

I want someway to be able to type, say, foo1234<tab> and have it expand to /foo/bar/baz/quux1234. This would greatly ease the typing of many command lines that have to deal with stuff in these directories.

(The paths are actually longer and traditional file-name completion requires multiple tabs to traverse the directory tree, which is a pain.)

Is that sort of thing possible? Anybody have any ideas how to do it?

I’m not a bash guru by any stretch…

With bash, you can use programmable completion to define functions that control how expansion is done for specific commands, but I don’t think you can redefine how the default kinds of completion are done, short of modifying the bash source code.

This leaves you with a few options. You could define a custom completion function, and then try to associate with all of the commands you commonly use. That strikes me as an ugly solution; even if you eventually get the function associated with nearly all of the commands you use, you’re still going to trip over commands for which completion doesn’t work from time to time. How annoying.

You could define a single command, which does nothing more than execute the command it receives as arguments. Now you’ve got only that one command to associate with the completion function, but everything you do for which you want this completion to work is going to look like “runmycmd ls blahblahblah.” Yuck.

Or, give up on doing something bash specific, and just write a simple shell script to handle the expansion. Something like:



#!/bin/sh
find /$1 -name \*$2 -print


Name it something short, say “q”, and now you can do “q foo 1234” to see an expansion of /foo/bar/baz/quux1234, and things like "ls q foo 1234" to feed the expansion as an argument to other commands. Not exactly what you wanted, but it may be less annoying than any of the options for doing what you really wanted. And it’s not bash-specific, so your tcsh users are happy too.

I suppose you could also do a mix of the last two, and associate the completion function with echo. Then you could do stuff like "ls echo foo1234&lt;tab&gt;" That would have the advantage of letting you see the expansion before you hit return.

If you’re still going to go down the custom completion route, you may want to check out the bash man page under “Programmable Completion,” and perhaps check out the completion definitions that come with subversion as an example of how to get it to work.

Yeah, I was afraid I’d just have to go with the `` route. Not an optimal solution but it will work easily enough.

I’m too lazy to read the whole thing to see if it really does what you want, but it seemed promising.

Maybe if I have some time later I will look at it again. It’s something I might find useful, too.

At first glance, the link that arseNal posted looks like it is exactly what is needed.
(I printed it to PDF and stashed it away for future reference :))
It describes a technique for having tab completion invoke your own black box function to come up with an arbitrary list of possible hits.

But…

Once your custom tab-completion black box function is invoked, how does it do its work efficiently?

You are providing something at the beginning of a path and something in the middle or end of a path. Your prefix provides some light guidance to the computer of which subset of the current tree of files to look at, but you could still potentially be looking at tens or hundreds of thousands of files.

Now once your tab-completion function is invoked, it would have to access a list of every possible file path that begins with “foo” and contains “1234”, looking for matches, assuming your rule is “Match as much as possible in my current directory, and then look for a path that contains the rest somewhere”

Standard directory searching will be unpleasant if you are at the top of a huge tree, as you can see if you use the “find” command. Therefore, you would have to have a way to index the paths of every file under the tree where you are working. I imagine you could periodically build an index file of paths using a cron job, and your function could grep it to come up with possible hits, but it isn’t a silver bullet.

If you are talking about one or two top-level directories where this behavior should work, then a script in those directories with whatever supporting stuff would work fine. If you want this to work across the entire filesystem seamlessly, then it might be a little harder.

haven’t read through it all yet, but this looks like it could help.

http://www.hypexr.org/bash_tutorial.php#completion

I thought there was some kind of special bash ( an addon or something similar ?) called bash completion. But I’m not sure if that was right.

This is the updatedb command, yes? Which builds the filename database that is accessed when using the locate command. And on Windows, isn’t this essentially what the “indexing service” does? (I’m not sure, as one of the first things I do is to turn off indexing on Windows boxen I use; the performance hit is just too noticable to me.)

I thought this was standard on most, if not all, *nix systems. My Debian box runs it via cron daily @6:25am (just finished not too long ago). And I think, based on my laptop’s observed behavior, Ubuntu is set to run it just after booting up. Granted, not a silver bullet (as you say), as it won’t catch any files created/deleted between runs.

I wish I knew more about bash’s tab-completion to suggest something elegant and efficient; I’d probably just write a shell script that relied on some combination of locate, cut, sed, etc., but it would lack the tab aspect and would be inferior. Useful, but inferior. :frowning:

Thanks, Digital Stimulus. I didn’t know about updatedb and locate. I am so dependent on find to do everything that I never even looked for the others.

It looks like a script that wraps locate and hooks into tab completion might just do the trick, though the arg might need to be a regex and some regex chars may need to be escaped to get past the shell.

You’re quite welcome. locate piped to grep (with regexes) is a wonderful thing.

That’s talking about bash’s programmable completion. While it’s true that it can be used to define custom means of auto-completion, the problem is that those custom functions can’t be generally associated with all commands. As far as I can tell, you can run “complete” to associate your custom method only with specific commands. Any commands you don’t specify continue to use the default compspec.

This lets you do things like write a function that looks through /etc/fstab for possible auto-completions for partial arguments to mount, and the like. While that’s kind of nifty, it’s not a very good solution for a problem that really demands changing how the default completion works for all commands.

Yep, that’s the exact limitation I ran into. I need something that can expand path names for any command, not as an argument to a particular command.

Well, I’m already pretty impressed that they allow you to customize it at all.

I suppose you can always do the customization thing, then write another script that associates the new behavior with all the commands you are likely to need it for. Surely it’s just a few commands, right? cp, mv, etc? Then just run that script anytime you want the behavior to go into effect. Or even put it in your .bashrc.

Of course, it seems a lot of work when you can just do other tricks that are almost as good, like maybe just write an alias that will cd you to the directory of the file you type.

Or just do the `` thing.