diff --git a/contrib/rsync b/contrib/rsync index f273e0d6..146c8bdc 100644 --- a/contrib/rsync +++ b/contrib/rsync @@ -70,7 +70,7 @@ _rsync() break fi done - [ "$shell" = ssh ] && _scp_remote_files "$cur" + [ "$shell" = ssh ] && _scp_remote_files fi ;; *) diff --git a/contrib/ssh b/contrib/ssh index 5e1035f5..a545406f 100644 --- a/contrib/ssh +++ b/contrib/ssh @@ -246,12 +246,14 @@ shopt -u hostcomplete && complete -F _sftp sftp # things we want to escape in remote scp paths _scp_path_esc="[][(){}<>\",:;^&\!$=?\`|\\ ']" +# Complete remote files with ssh. If the first arg is -d, complete on dirs +# only. Returns paths escaped with three backslashes. _scp_remote_files() { local IFS=$'\t\n' # remove backslash escape from the first colon - local cur=${1/\\:/:} + cur=${cur/\\:/:} local userhost=${cur%%?(\\):*} local path=${cur#*:} @@ -264,23 +266,45 @@ _scp_remote_files() 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=( "${COMPREPLY[@]}" $( ssh -o 'Batchmode yes' $userhost \ - command ls -aF1d "$path*" 2>/dev/null | \ - sed -e 's/'$_scp_path_esc'/\\\\\\&/g' -e 's/[*@|=]$//g' \ - -e 's/[^\/]$/& /g' ) ) + local files + if [ "$1" = -d ] ; then + # escape problematic characters; remove non-dirs + files=$( ssh -o 'Batchmode yes' $userhost \ + command ls -aF1d "$path*" 2>/dev/null | \ + sed -e 's/'$_scp_path_esc'/\\\\\\&/g' -e '/[^\/]$/d' ) + else + # escape problematic characters; remove executables, aliases, pipes + # and sockets; add space at end of file names + files=$( ssh -o 'Batchmode yes' $userhost \ + command ls -aF1d "$path*" 2>/dev/null | \ + sed -e 's/'$_scp_path_esc'/\\\\\\&/g' -e 's/[*@|=]$//g' \ + -e 's/[^\/]$/& /g' ) + fi + COMPREPLY=( "${COMPREPLY[@]}" $files ) } # This approach is used instead of _filedir to get a space appended # after local file/dir completions, and -o nospace retained for others. -# Args: 1=prefix to strip (optional) +# If first arg is -d, complete on directory names only. The next arg is +# an optional prefix to add to returned completions. _scp_local_files() { local IFS=$'\t\n' - COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* 2>/dev/null | \ - sed -e "s/$_scp_path_esc/\\\\&/g" -e 's/[*@|=]$//g' \ - -e 's/[^\/]$/& /g' -e "s/^/$1/") ) + + local dirsonly=false + if [ "$1" = -d ]; then + dirsonly=true + shift + fi + + if $dirsonly ; then + COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* 2>/dev/null | \ + sed -e "s/$_scp_path_esc/\\\\&/g" -e '/[^\/]$/d' -e "s/^/$1/") ) + else + COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* 2>/dev/null | \ + sed -e "s/$_scp_path_esc/\\\\&/g" -e 's/[*@|=]$//g' \ + -e 's/[^\/]$/& /g' -e "s/^/$1/") ) + fi } # scp(1) completion @@ -321,7 +345,7 @@ _scp() _expand || return 0 if [[ "$cur" == *:* ]]; then - _scp_remote_files "$cur" + _scp_remote_files return 0 fi