3

I need my bash script to get function names from user. The script should assist the users by auto completing the functions defined inside the same bash script file.

Eg:

myBash.sh

#!/usr/bash
function func1()
{
    echo "In func1 func"
}

function func2()
{
    echo "In func2 func"
}

function myFunc1()
{
    echo "In myFunc1 func"
}

while [ 1 ]
do
    echo -n "cmd>"
    read $userCmd
    $userCmd
done

]$ ./mybash.sh
cmd> my<tab><tab>
myFunc1

cmd> func<tab><tab>
func1 func2

This is my required output. How to accompany this?

Ashwin
  • 993
  • 1
  • 16
  • 41
  • Half an answer: use the `-e` option to `read` to use `readline` to process the input (which will include command completion). The harder half is to get `readline` to use only the locally defined functions as potential completions for a command name. – chepner Jul 16 '14 at 14:18
  • 1
    apparently it's real pain to do, and I don't know if the bug described in [this answer](http://stackoverflow.com/questions/4726695/bash-and-readline-tab-completion-in-a-user-input-loop) has been corrected or not – Aserre Jul 16 '14 at 14:54
  • @Ploutox - I was searching for that post, but couldn't remember how to find it. That bug bit me too when I tried to do this. Great find! – ghoti Jul 16 '14 at 14:59

1 Answers1

2

This workaround should do the trick.

    #!/bin/bash

    func1() {
        echo "You are in func1: $@"
    }
    
    func2() {
        echo "You are in func2: $@"
    }

    myFunc1() {
        echo "You are in myFunc1: $@"
    }
    
    # usage: autocomplete "word1 word2 ..."
    autocomplete() {
         # try to autocomplete the last word, and 
         # keep a record of the rest of the input
         OTHER_WORDS="${READLINE_LINE% *} " 
         if [[ ${#OTHER_WORDS} -ge ${#READLINE_LINE} ]]; then 
             # if there is only 1 word...
             OTHER_WORDS="" 
         fi
         
         # the -W flag tells compgen to read autocomplete
         # from the 1st argument provided, then we
         # evaluate the last word of the current line
         # through compgen
         AUTOCOMPLETE=($(compgen -W $1 "${READLINE_LINE##* }"))
         if [[ ${#AUTOCOMPLETE[@]} == 1 ]]; then 
             # if there is only 1 match, substitute it
             READLINE_LINE="$OTHER_WORDS${AUTOCOMPLETE[0]} "
             # position the cursor at the end of the word
             READLINE_POINT=${#READLINE_LINE}
         else
             #...otherwise print the possibilities
             echo -e "cmd> $READLINE_LINE\n${AUTOCOMPLETE[@]}" 
         fi
    }
    
    # list the values to be autocompleted
    MYFUNC="func1 func2 myFunc1" 
    # enable line editing
    set -o emacs
    # call autocomplete when TAB is pressed
    bind -x '"\t":"autocomplete \$MYFUNC"';   
    while read -ep "cmd> "; do
        # history is just a nice bonus
        history -s $REPLY
        eval ${REPLY}
    done

To try it :

    ]$ ./mybash.sh
    cmd> my<tab>
    cmd> myFunc1
    
    cmd> func<tab>
    func1 func2
    
    cmd> func1 hello, world!
    You are in func2: hello, world!

    cmd> func1 my<tab>
    cmd> func1 myFunc1

As mentioned in my previous comment, have a look at this question. It uses a nice trick to auto detect all inner functions in order to use it as auto-complete values.

Eric
  • 2,539
  • 18
  • 23
Aserre
  • 4,916
  • 5
  • 33
  • 56