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:
- I SSH into my remote server, find the file and copy its full path.
- 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.