Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'd like to instruct bash to use a special method to perform completion on certain directory names. For example, bash would call a program of mine to perform completion if a path starts with "$$", and perform completion normally otherwise.

Is this at all possible? How would you implement it?

Bounty: I'd really appreciate an answer to that question. The goal is to allow autojump to complete paths for all commands when the user starts them with a certain prefix. So for example when copying a file from a far directory, you could type:

cp $$patern + <Tab>

and autojump would complete

cp /home/user/CompliCatedDireCTOry/long/path/bla/bla

and you would just have to add where you want to put the file. Of course I can use ott's comment to add it to a few specific commands, but if anyone has a better idea, I'd be very grateful.

share|improve this question

migrated from stackoverflow.com Sep 8 '11 at 8:03

This question came from our site for professional and enthusiast programmers.

    
See the section "Programmable Completion" in the bash man page. –  ott-- Sep 7 '11 at 11:35
    
If you vote to close my question, please explain why. –  Peltier Sep 7 '11 at 11:44
2  
"complete" supports "-G pattern" to match your $$ an the beginning and "-F func" to call your own function, but it needs one or more command names to work. –  ott-- Sep 7 '11 at 13:14
    
Why not just use an environment variable? For example: cp $prefix/file /path/to/dest/ –  Xenoactive Sep 9 '11 at 19:58
    
@Xenoactive: because autojump is fully automated, and works on more than one path. Using manually set environment variables doesn't help. Or maybe you have a powerful idea that I don't understand? –  Peltier Sep 10 '11 at 10:34

3 Answers 3

up vote 3 down vote accepted
+100

You can do this by overriding the default binding for TAB(^i). First you need to override the TAB binding, then you need to build a function that calls your command, lastly you need to take the output from that command and update the variable that contains the current command line.

This function takes the current command line and changes the last two characters to 'huugs'.

function my_awesome_tab_completion_function () {
  set -- $READLINE_LINE
  command="$1"
  shift
  argument="$*"
  argument_length=$(echo -n $argument | wc -c)
  if echo $argument | grep '^$$' >/dev/null 2>&1; then
    new_argument=$(echo $argument | sed 's/..$/huugs/') # put your autojump here
  else
    new_argument=$(compgen -d $argument)
  fi
  new_argument_length=$(echo -n $new_argument | wc -c)
  READLINE_POINT=$(( $new_argument_length - $argument_length + $READLINE_POINT ))
  READLINE_LINE="$command $new_argument"
}

For your example you'd probably want to change the new_argument line to look like this:

  new_argument=$(autojump $argument)

Now override the ^i binding:

$ bind -x '"\C-i"':'my_awesome_tab_completion_function'

Now test that it works:

$ cd /ro<TAB>
changes my command to:
$ cd /root

so normal completion still works, you can test the $$ part by doing cd $$... etc

If you run into issues turn on verbose mode:

$ set -x

It will print out everything the function is doing.

I tested this on Ubuntu 11 using bash 4.2.8(1)-release (the default).

share|improve this answer
    
It seems to me that this works only on the first parameter of the command, and can also do weird stuff if the tab is used in unexpected ways (for example after a $$$ or after the command name), or am I wrong ? –  harrymc Sep 10 '11 at 22:35
    
Well it is passing $* so it will 'work' on more than just the first parameter(I don't know autojump at all so I'm not sure if it can take multiple arguments). You could shift to the end and only send the last to autojump, or use READLINE_POINT to determine where the cursor is among the command arguments and send only that 'word' to autojump too. –  polynomial Sep 12 '11 at 5:47
    
Your solution is interesting, but you have to admit that taking over the tab key is a bit extreme. There is no way that you could anticipate all the possible cases. –  harrymc Sep 12 '11 at 6:48
    
Thanks for your answer, that's a nice alternative solution, and it does address the "for all commands" part of my question. I agree with harrymc that it's a bit extreme, though... I guess if nobody comes up with a better solution I'll give it a try and see if it is viable. –  Peltier Sep 12 '11 at 19:30
    
Ya I agree with harrymc too, but was trying to answer the question as asked. Personally I would just bind this to another control key like ^G or something so I could leave tab alone but still use this functionality. –  polynomial Sep 12 '11 at 23:57

This may be a good tutorial to look at, http://www.debian-administration.org/articles/317

share|improve this answer
    
It's a good tutorial, but it talks about completing particular commands. I want to alter the way bash completes paths, no matter which command is used. –  Peltier Sep 7 '11 at 11:26
2  
Ah I see, I can't say that I've heard of such ways, but I'm no expert and only knew of the link I sent. I would be interested in such a way though. Might be useful. +1 to your question if I could. –  PhiXhiP Sep 7 '11 at 11:37

The bash completion routine can be programmed as a shell script.

Here is an example of a shell-script that will replace in any parameter $$[Tab] by my replacement string, but only for the specific command mycommand and only if the parameter is exactly "$$" :

_mycomplete()
{
        if [ ${COMP_WORDS[COMP_CWORD]} == \$\$ ]
        then
                COMPREPLY='my replacement string'
        fi
}
complete -o default -o bashdefault -F _mycomplete mycommand

You must source the script to bash via source <file-name> (or the dot command) to start it working, and then :

mycommand $$[Tab] -> mycommand my replacement string
mycommand $$$[Tab] -> mycommand $$$ (beep)
mycommand whatever[Tab] -> (will complete "whatever" in the normal bash manner)

To always have it working for some or all users, include this in one of the bash profile routines.

The problem with the complete command, is that it will work only for one or more names of commands, that are specified as parameters. One could simply give it the list of all the commands that could possibly be used by the users, or in desperate cases expand /bin/* /usr/bin/* ~/bin/*.

Tested on CentOS 5.5.

This simple script is based on the sources I have listed in my other answer - the one which was deleted by the moderator studiohack. If interested, just ask him to undelete it.

share|improve this answer
    
Thanks for your answer. My question wasn't really about that since I new it was possible, but won't complete all commands. However I think that if polynomial's solution doesn't turn out to be acceptable, I'll implement something like that and try to target the most common commands. –  Peltier Sep 12 '11 at 19:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.