When using Vim you may find a situation where you want to duplicate a file, without jumping to the command line. While there are lots of instructions online about how to copy a file from one directory to another in Vim when in netrw:
mt
mark targetmf
mark filemc
make copy
A problem arises when the target (destination directory) is the same as the source directory. In this case we get the error
**error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work!
Based on my reading there may be a newer version of netrw that allows this however I wanted to a solution that works without upgrading netrw.
While I could use the command :write my-new-file-name.md
this writes the new version into the current directory (see :help current-directory
), which is not necessarily the same directory where the original file is located.
Duplicate the Current File into the Same Directory
The solution I’ve found for my own workflow is running the following command, which duplicates the file you are currently viewing into the same directory as the original.
:!cp '%:p' '%:p:h/%:t:r-copy.%:e'
You’ll notice this solution doesn’t actually use netrw but until I find a better solution, this is what I’m using.
Example
If we are currently viewing the file at /Users/sal/jokes/captain-hook-sneakers.md
. Running the command
:!cp '%:p' '%:p:h/%:t:r-copy.%:e'
is the same as running
:!cp '/Users/sal/jokes/captain-hook-sneakers.md' '/Users/sal/jokes/captain-hook-sneakers-copy.md`
Explanation
To break this down:
!
runs a shell command (see:help !cmd
)cp
command line copy'%:p'
(expands to'/Users/sal/jokes/captain-hook-sneakers.md'
)%
the current file (see:help _%
):p
modifier for the full path (see:help %:p
)
'%:p:h/%:t:r-copy.%:e'
(expands to'/Users/sal/jokes/captain-hook-sneakers-copy.md'
)%:p:h
(expands to/Users/sal/jokes
)%
the current file:p
modifier for the full path:h
modifier to remove the last component and any separators (see:help %:h
)/
the directory separator%:t:r
(expands tocaptain-hook-sneakers
)%
the current file:t
modifier for the filename with extension (see:help %:t
):r
modifier to remove the extension (see:help %:r
)-copy
our additional text to differentiate this from the original file.
the period to proceed the file extension%:e
(expands tomd
)%
the current file:e
the extension of the filename (see:help %:e
)
Paths with Spaces
You’ll notice that we wrap both our paths in single quotes ('
). We do this in case our path includes spaces.
e.g. If our file is at /Users/sal/My Jokes/captain-hook-sneakers.md
, and we tried to run the shell command
cp /Users/sal/My Jokes/captain-hook-sneakers.md /Users/sal/My Jokes/captain-hook-sneakers-copy.md
it would fail because of the spaces in the filepath.
By wrapping our filepaths in single quotes ('
) we protect against this.
cp '/Users/sal/My Jokes/captain-hook-sneakers.md' '/Users/sal/My Jokes/captain-hook-sneakers-copy.md'
Silence
When you run
:!cp '%:p' '%:p:h/%:t:r-copy.%:e'
you’ll see the expanded version of the command along with the message
Press ENTER or type command to continue
While in some cases this can be helpful, I prefer to skip over this output message. You can accomplish this by wrapping the command in a :silent exec
call (see :help silent
)
:silent exec "!cp '%:p' '%:p:h/%:t:r-copy.%:e'"
Create a Mapping
The following mapping will allow you to use your Leader key + c to copy the current file.
nnoremap <silent> <Leader>c :silent exec "!cp '%:p' '%:p:h/%:t:r-copy.%:e'"<cr>
See :help nnoremap
, :help <silent>
, :help <Leader>
Updated Mapping
I like visual feedback when a command runs and this command above had none. We can extend this mapping to display a message like
Copied myfile.txt to myfile-copy.txt
The updated mapping that includes this output is
nnoremap <silent> <Leader>c :clear<bar>silent exec "!cp '%:p' '%:p:h/%:t:r-copy.%:e'"<bar>redraw<bar>echo "Copied " . expand('%:t') . ' to ' . expand('%:t:r') . '-copy.' . expand('%:e')<cr>
See :help map-bar
, :help redraw
:help expand
Known Problems
If the file being copied does not have an extension, the new version will have a trailing period. e.g. myfile
copies to myfile-copy.
Other solution is use the command:
:write %:p:h/mi_copy_file.extension
in the file .vimrc:
map \c :write %:p:h/
Nice guide! Great usage of `expand`.
Here’s one command-based approach I’ve used that’s similar: