Keywords:
I love Vim! It is so easy enough to edit a remote file with my local Vim through the Secure Copy protocol:
$ vi scp://user@remote.host.com/projects/foo/bar.py
However, I often find myself wishing that bash completion was available to expand/complete paths on the remote system. Furthermore, when editing files outside of my home directory hierarchy, I have to remember to add an extra slash after the host name, e.g.:
$ vi scp://user@remote.host.com//var/www/html/index.htm
A solution is to write a custom wrapper function that takes scp-style remote file paths, converts them into Vim-style remote file paths, and then invokes Vim on them.
Then we can make use of the existing scp
bash completion for our wrapper function, and we get the side-benefit of not needing to juggle two different remote file path syntaxes.
The following code implements this solution. The function "vscp" is the wrapper function, while the line "complete -F _scp $nospace vscp" applies the "scp" completion function to the wrapper function. The code below needs to be sourced into your shell or placed into "~/.bashrc".The "scp" completion function, "_scp() should already be defined if you have bash completion installed and sourced (and you should!), but just in case, it is conditionally defined here.
#! /bin/bash
function vscp() {
if [[ -z $1 ]]
then
echo "usage: vscp [[user@]host1:]file1 ... [[user@]host2:]file2"
return
fi
declare -a targs=()
for iarg in $@
do
targ="scp://$(echo $iarg | sed -e 's@:/@//@' | sed -e 's@:@/@')"
targs=("${targs[@]}" $targ)
done
vi ${targs[@]}
}
## Following function is from ".bash_completion". If it
## is not already defined/sourced, we define it here.
if [[ !$(declare -F _scp) ]]
then
_scp()
{
local cur userhost path
local IFS=$'\t\n'
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
_expand || return 0
if [[ "$cur" == *:* ]]; then
# 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
[[ "$cur" == */* ]] || _known_hosts -c -a
COMPREPLY=( ${COMPREPLY[@]} $( command ls -aF1d $cur* \
2>/dev/null | sed \
-e 's/[][(){}",:;^&!$&=?`|\ ]/\\&/g' \
-e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
return 0
}
fi
complete -F _scp -o nospace vscp
The code is obviously pretty basic, and can be elaborated further (e.g., by passing custom options/arguments to Vim).
Once you have a remote file open in Vim, the command ":E" or ":Explorer" will open up Vim's native remote filesystem browser. Once you have set up the function presented above, you can also invoke Vim's native remote filesystem browser by passing in a directory path as an argument, being sure to include the trailing backslash:
$ vscp user@remote.host.com:/var/html/
Of course, the obvious alternative to all of this is to login to the remote system and just edit the files through the ssh connection. Vim is, after all, ubiquitious and installed by default on most (decent) operating systems (except for the one that shall not be named). And, to be honest, this is what I do most of the time when making minor edits or tweaks.
However, there still are some benefits to using my local Vim versus a remote one over an ssh connection on a terminal emulator, especially when I have a major editing/debugging session: speed/responsiveness, and much nicer syntax coloration (16 million colors vs. the 16 colors to which I am limited using OS X's Terminal). These may seem like minor advantages, but both make a huge difference when trawling through hundreds of lines of code!
feed
4 Comments
Tab-completion over remote
Submitted by lozenge (not verified) on
Tab-completion over remote hosts is just one of many useful features in Zsh... no custom wrappers necessary.
Yep, I've heard lots of good
Submitted by Jeet Sukumaran on
Yep, I've heard lots of good things about zsh (one biggie for me: insert/normal mode indication at the prompt when using vi-mode line editing). About once a month, I consider trying it out. But bash is the default shell for almost every system that I work with, and I really do not feel like using a different shell on my personal machine and/or building and installing zsh on all my remote systems.
Check if _scp() is defined or not
Submitted by Evaryont (not verified) on
A possible improvement that could be made is to have bash check if the function is defined or not. That way you can include the method while feeling safe about stomping over some function.
I'm sure you can do this in zsh, but I don't know how to do it in bash. (Too much time spent in zsh, I guess. I've forgotten a lot of the bash-ims while learning the zsh-isms.)
Good idea. You could use
Submitted by Jeet Sukumaran on
Good idea. You could use ``declare -F _scp`` to see if it defined in .bash. I will edit the code and include your suggestion. Thanks.
Add new comment