Bash TAB-completion inside double-quoted string

2019-05-11 04:36发布

问题:

Problem

I'm writing a Twitter client for the command line (in C). I'm currently working on doing TAB-completion for Twitter screen names, like so:

tweet "@s<TAB>
@sourcebits @spolsky

However, I can't get it to work mid-string, e.g.:

tweet "Foo bar @s<TAB>

since Bash treats the string as one word. I couldn't find anything in the Bash man page suggesting a simple fix, so I decided to hack around it. Below is my half-done solution to the problem. I simply split the incoming string by spaces, take the last word (simplification, for now) and send it to compgen (the variable $last_word below). However, I need to append $prefix to the beginning of the string that the TAB-completion produces, as it replaces the whole input string (remember: it's treated as one word). That's where I'm stuck.

Question

How can this be done?

Code etc.

__cltwitter_complete () {
  local cache="$HOME/.cltwitter_users.cache"
  local string=${COMP_WORDS[COMP_CWORD]}
  local last_word=${string##* }
  local prefix=${string% *}

  COMPREPLY=()

  #if [ ! -f ${cache} ] || [ "`find ${cache} -mmin +60`" != "" ]; then
  #  cltwitter-update-cache
  #fi

  if [[ "$last_word" == \"@* ]]; then  # if word is beginning of a quotation
    last_word=${last_word:2}
  elif [[ "$last_word" == @* ]]; then  # if user put '@' in front
    last_word=${last_word:1}
  fi

  COMPREPLY=( $( compgen -W "`cat $cache`" -P @ -- $last_word ) )
}

complete -F __cltwitter_complete tweet

Relevant section from the Bash man page:

COMP_WORDS

An array variable (see Arrays below) consisting of the individual words in the current command line. The words are split on shell metacharacters as the shell parser would separate them. This variable is available only in shell functions invoked by the programmable completion facilities.

回答1:

Look at programmable completion: http://www.faqs.org/docs/bashman/bashref_103.html



回答2:

Do you really need the quotes?

For example, your command could be:

tweet Foo bar @s<TAB>

Then @s would simply be the word you are trying to complete, and you can search for that. Inside the script, you can still get at the entire string with $*.

You'll have to be more careful invoking now, though, as you can't use special characters such as ! and ( ) anymore without having to escape them with a backslash.