Is it possible to get tab completion with sftp?

Sometimes I need to quickly copy a file from my remote server to my local machine. Here's my current workflow:

  1. I SSH into my remote server, find the file and copy its full path.
  2. I open new terminal tab and type:

sftp user@hostname:/path/to/file (where /path/to/file is the path I previously copied)

It's not such a pain but it would be really nice if I could skip step 1 and find the path to the file using tab completion directly when typing the sftp command.

To illustrate, I could start typing sftp user@hostname:/ press TAB and get a listing of folders in /. I could then go on typing ho press TAB and it would auto-complete to home, etc. etc.

I'm not sure if such a feature exists and otherwise, is it theoretically possible to write a custom tab completion script as described? Any pointers on where to start?


Thanks to shellholic's answer, I was able to make it (somewhat) work for sftp. First, create the file /etc/bash_completion.d/sftp with the following content:

# custom sftp(1) based on scp
# see http://askubuntu.com/questions/14645/is-it-possible-to-get-tab-completion-with-sftp
#
_sftp()
{
    local configfile cur userhost path prefix

    COMPREPLY=()
    cur=`_get_cword ":"`

    _expand || return 0

    if [[ "$cur" == *:* ]]; then
        local IFS=$'\t\n'
        # remove backslash escape from :
        cur=${cur/\\:/:}
        userhost=${cur%%?(\\):*}
        path=${cur#*:}
        # unescape spaces
        path=${path//\\\\\\\\ / }
        if [ -z "$path" ]; then
            # default to home dir of specified user on remote host
            path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
        fi
        # escape spaces; remove executables, aliases, pipes and sockets;
        # add space at end of file names
        COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
            command ls -aF1d "$path*" 2>/dev/null | \
            sed -e "s/[][(){}<>\",:;^&\!$=?\`|\\ ']/\\\\\\\\\\\\&/g" \
            -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
        return 0
    fi

    if [[ "$cur" = -F* ]]; then
        cur=${cur#-F}
        prefix=-F
    else
        # Search COMP_WORDS for '-F configfile' or '-Fconfigfile' argument
        set -- "${COMP_WORDS[@]}"
        while [ $# -gt 0 ]; do
            if [ "${1:0:2}" = -F ]; then
                if [ ${#1} -gt 2 ]; then
                    configfile="$(dequote "${1:2}")"
                else
                    shift
                    [ "$1" ] && configfile="$(dequote "$1")"
                fi
                break
            fi
            shift
        done

        [[ "$cur" == */* ]] || _known_hosts_real -c -a -F "$configfile" "$cur"
    fi
    # This approach is used instead of _filedir to get a space appended
    # after local file/dir completions, and $nospace retained for others.
    local IFS=$'\t\n'
    COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* 2>/dev/null | sed \
        -e "s/[][(){}<>\",:;^&\!$=?\`|\\ ']/\\\\&/g" \
        -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' -e "s/^/$prefix/") )

    return 0
}
complete -o nospace -F _sftp sftp

Then in bash you need to execute . /etc/bash_completion.d/sftp in order to load the script.

All I really did was copy/paste the scp completion script from /etc/bash_completion.d/ssh and replace scp occurences with sftp.


Another alternative: use lftp instead, which has superb built in tab completion (but that'll be once you start it, not in the shell.) It can talk sftp and many other protocols.


Don't use sftp, use scp and it work. You will need an ssh key.


I hear that a program called with-readline will allow the standard OpenSSH command line program sftp to utilize tab completion. I cannot verify that it works, but I've been wanting that same functionality for years now.

info on with-readline and sftp: http://www.greenend.org.uk/rjk/2005/withreadline.html

with-readline link: http://www.greenend.org.uk/rjk/2005/with-readline-0.1.tar.bz2

Let me know how it works for you.