Search code examples
bashshellbash-completion

Writing a bash completion script similar to scp


I have a script I thought I would allow completions via Bash completion.

The script takes a quoted string argument and then a file or server e.g.

my_cmd "something"  stuff

I thought I could leverage the same completion as the scp command by:

have my_cmd &&
_my_cmd()
{
    . /usr/share/bash-completion/completions/scp >/dev/null 2>&1 && complete -F _scp $1 && return 124
} && complete -F _my_cmd my_cmd

Which works, however this appends a colon to known servers. Not really knowing much about how the bash-completion works have you any better ideas to accomplish this?


Solution

  • If you look in the scp completion, it's using _known_hosts_real from /usr/share/bash-completion/bash-completion:

    # Helper function for completing _known_hosts.
    # This function performs host completion based on ssh's config and known_hosts
    # files, as well as hostnames reported by avahi-browse if
    # COMP_KNOWN_HOSTS_WITH_AVAHI is set to a non-empty value.  Also hosts from
    # HOSTFILE (compgen -A hostname) are added, unless
    # COMP_KNOWN_HOSTS_WITH_HOSTFILE is set to an empty value.
    # Usage: _known_hosts_real [OPTIONS] CWORD
    # Options:  -a             Use aliases
    #           -c             Use `:' suffix
    #           -F configfile  Use `configfile' for configuration settings
    #           -p PREFIX      Use PREFIX
    # Return: Completions, starting with CWORD, are added to COMPREPLY[]
    

    So, what you probably want is:

    _my_cmd() {
        local cur prev words cword
        _init_completion || return
       _known_hosts_real -a "$cur"
    } && complete -F _my_cmd my_cmd
    

    This works for me, at least. I admit, I don't generally write these, but it seems consistent with how other completions work. Here is another related example if you want to do some more shell foo with your completions:

    https://unix.stackexchange.com/questions/136351/autocomplete-server-names-for-ssh-and-scp

    Edited:

    Here's for hosts and files, mostly inspired by what _scp_local_files does:

    _my_cmd() {
        local cur prev words cword
        _init_completion || return
    
       _known_hosts_real -a "$cur"
    
        COMPREPLY+=( $( ls -aF1dL $cur* 2>/dev/null | \
            sed -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' -e "s/^/$prefix/") )
    
    } && complete -F _my_cmd my_cmd
    

    Again, this is just taking the stuff you want from the _scp completion and making your own. This works for me, but the directory name completion is a little wonky.