bash tab completion with spaces

前端 未结 3 1292
失恋的感觉
失恋的感觉 2021-02-06 11:44

I\'m having a problem with bash-completion when the possible options may contain spaces.

Let\'s say I want a function which echoes the first argument:

fu         


        
3条回答
  •  面向向阳花
    2021-02-06 12:10

    Okay, this crazy contraption draws heavily on rici’s solution, and not only fully works, but also quotes any completions that need it, and only those.

    pink() {
        # simulating actual awk output
        echo "nick mason"
        echo "syd-barrett"
        echo "david_gilmour"
        echo "roger waters"
        echo "richard wright"
    }
    
    _test() {
      cur=${COMP_WORDS[COMP_CWORD]}
      mapfile -t patterns < <( pink )
      mapfile -t COMPREPLY < <( compgen -W "$( printf '%q ' "${patterns[@]}" )" -- "$cur" | awk '/ / { print "\""$0"\"" } /^[^ ]+$/ { print $0 }' )
    }
    
    complete -F _test test
    

    So as far as I could test it, it fully implements ls-like behavior, minus the path-specific parts.

    Verbose example

    Here’s a more verbose version of the _test function, so it becomes a bit more understandable:

    _test() {
      local cur escapedPatterns
      cur=${COMP_WORDS[COMP_CWORD]}
      mapfile -t patterns < <( pink )
      escapedPatterns="$( printf '%q ' "${patterns[@]}" )"
      mapfile -t COMPREPLY < <( compgen -W "$escapedPatterns" -- "$cur" | quoteIfNeeded )
    }
    
    quoteIfNeeded() {
      # Only if it contains spaces. Otherwise return as-is.
      awk '/ / { print "\""$0"\"" } /^[^ ]+$/ { print $0 }'
    }
    

    None of this is even remotely optimized for efficiency. Then again, this is only tab completion, and it’s not causing a noticeable delay for any reasonably large list of completions.

    It works by:

    1. Pulling the awk output into an array, using mapfile.
    2. Escaping the array and putting it into a string.
    3. Having a single space behind the %q as a separation marker.
    4. Quoting $cur, Very important!
    5. Quoting the output of compgen. And only if it contains spaces.
    6. Feeding that output into COMPREPLY, using another mapfile call.
    7. Not using -o filenames.

    And it only works with all those tricks. It fails if even a single one is missing. Trust me; I’ve tried. ;)

提交回复
热议问题