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 have a bunch of bash completion scripts set up (mostly using bash-it and some manually setup).

I also have a bunch of aliases setup for common tasks like gco for git checkout. Right now I can type git checkout dTab and develop is completed for me but when I type gco dTab it does not complete.

I'm assuming this is because the completion script is completing on git and it fails to see gco.

Is there a way to generically/programmatically get all of my completion scripts to work with my aliases? Not being able to complete when using the alias kind of defeats the purpose of the alias.

share|improve this question
    
What OS and bash are you using? I am on Ubuntu 11.10 and bash 4.2.10(1)-release (x86_64-pc-linux-gnu) and I have this functionaity built in to my shell for my many aliases. btw bash --version to get this (don't use -v, different output). –  Michael Durrant Jun 16 '12 at 13:14
    
Sorry missed taht bit of info - OSX Lion, GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11) –  dstarh Jun 18 '12 at 13:10
1  
@killermist: unless I’m completely mistaken, zsh doesn’t complete aliased commands out of the box either. Implementing a function that adds defined aliases to completion appears to be much easier than for bash, though, as zhs’s completion system seems both more powerful and more straightforward than bash’s. –  kopischke Jun 23 '12 at 19:22
    
Cross site duplicate: stackoverflow.com/questions/342969/… –  Ciro Santilli Jan 20 at 8:57
    
@ciro I agree that the question is a bit dupe but IMO this answer addresses the Subject of the question, not JUST the example in the question. –  dstarh Jan 20 at 14:03
show 2 more comments

1 Answer

up vote 15 down vote accepted

The following code, adapted from this Stack Overflow answer and this Ubuntu Forums discussion thread will add completions for all your defined aliases:

# Automatically add completion for all aliases to commands having completion functions
function alias_completion {
    local namespace="alias_completion"

    # parse function based completion definitions, where capture group 2 => function and 3 => trigger
    local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)'
    # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments
    local alias_regex="alias ([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'"

    # create array of function completion triggers, keeping multi-word triggers together
    eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))"
    (( ${#completions[@]} == 0 )) && return 0

    # create temporary file for wrapper functions and completions
    rm -f "/tmp/${namespace}-*.tmp" # preliminary cleanup
    local tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}.tmp")" || return 1

    # read in "<alias> '<aliased command>' '<command args>'" lines from defined aliases
    local line; while read line; do
        eval "local alias_tokens=($line)" 2>/dev/null || continue # some alias arg patterns cause an eval parse error 
        local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }"

        # skip aliases to pipes, boolan control structures and other command lists
        # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters)
        eval "local alias_arg_words=($alias_args)" 2>/dev/null || continue

        # skip alias if there is no completion function triggered by the aliased command
        [[ " ${completions[*]} " =~ " $alias_cmd " ]] || continue
        local new_completion="$(complete -p "$alias_cmd")"

        # create a wrapper inserting the alias arguments if any
        if [[ -n $alias_args ]]; then
            local compl_func="${new_completion/#* -F /}"; compl_func="${compl_func%% *}"
            # avoid recursive call loops by ignoring our own functions
            if [[ "${compl_func#_$namespace::}" == $compl_func ]]; then
                local compl_wrapper="_${namespace}::${alias_name}"
                    echo "function $compl_wrapper {
                        (( COMP_CWORD += ${#alias_arg_words[@]} ))
                        COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1})
                        $compl_func
                    }" >> "$tmp_file"
                    new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }"
            fi
        fi

        # replace completion trigger by alias
        new_completion="${new_completion% *} $alias_name"
        echo "$new_completion" >> "$tmp_file"
    done < <(alias -p | sed -Ene "s/$alias_regex/\1 '\2' '\3'/p")
    source "$tmp_file" && rm -f "$tmp_file"
}; alias_completion

For simple (command only, no arguments) aliases it will assign the original completion function to the alias; for aliases with arguments, it creates a wrapper function that inserts the extra arguments into the original completion function.

Unlike the scripts it has evolved from, the function respects quotes both for the alias command and its arguments (but the former have to be matched by the completion command, and cannot be nested), and it should reliably filter out aliases to command lists and pipes (which are skipped, as it is impossible to find out what to complete in them without re-creating the complete shell command line parsing logic).

Usage

Either save the code as a shell script file and source that in, or copy the function wholesale into, .bashrc (or your pertinent dot file). The important thing is to call the function after both bash completion and alias definitions have been set up (the code above calls the function right after its definition, in a “source and forget” spirit, but you can move the call anywhere downstream if that suits you better). If you don’t want the function in your environment after it exits, you can add unset -f alias_completion after calling it.

Troubleshooting

If you get an error complaining about "too few X's", modify the following line:

local tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}.tmp")" || return 1

to

local tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}XXX.tmp")" || return 1

, as man mktemp indicates.

share|improve this answer
1  
How would I go about installing that script? –  Oliver Salzburg Jun 16 '12 at 10:45
1  
@OliverSalzburg: you’ll have to process it in one of your shell profile files, crucially after bash completion – that would probably make it ~/.bashrc. Either store it as a shell script file and source it (. /path/to/alias_completion.sh), or copy and paste the code wholesale. –  kopischke Jun 16 '12 at 12:04
1  
@OliverSalzburg: added usage instructions (didn’t notice right away you are not the OP). –  kopischke Jun 16 '12 at 12:27
    
As would likely have been pertinent info this is on mac osx and is not working for me. –  dstarh Jun 18 '12 at 13:23
    
@dstarh: so am I – the function works fine with the same configuration as you have on my machine. How exactly is it not working for you (try calling the function in the shell after it has been sourced, that should show the error messages, if any)? –  kopischke Jun 18 '12 at 13:43
show 12 more comments

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.