• Skip to primary navigation
  • Skip to main content
Sal Ferrarello
  • About Sal Ferrarello
  • Speaking
  • Connect
    Mastodon GitHub Twitter (inactive)
You are here: Home / Solution / Vim netrw duplicate file

Vim netrw duplicate file

Last updated on May 20, 2019 by Sal Ferrarello

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 target
  • mf mark file
  • mc 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 to captain-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 to md)
    • % 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.

Sal Ferrarello
Sal Ferrarello (@salcode)
Sal is a PHP developer with a focus on the WordPress platform. He is a conference speaker with a background including Piano Player, Radio DJ, Magician/Juggler, Beach Photographer, and High School Math Teacher. Sal can be found professionally at WebDevStudios, where he works as a senior backend engineer.

Share this post:

Share on TwitterShare on FacebookShare on LinkedInShare on EmailShare on Reddit
Warning! This is a draft, not a finalized post. See full draft disclosure.

Filed Under: Computing, Draft, Solution Tagged With: netrw, vim

Reader Interactions

Comments

  1. John Sánchez Alvarez says

    November 15, 2020 at 12:30 am

    Other solution is use the command:

    :write %:p:h/mi_copy_file.extension

    in the file .vimrc:
    map \c :write %:p:h/

    Reply
  2. Dave Hulihan says

    March 19, 2021 at 2:19 pm

    Nice guide! Great usage of `expand`.

    Here’s one command-based approach I’ve used that’s similar:

    
    " Duplicate creates a copy of a file in the same directory as the original.
    "   * `dstFilename` should not contain slashes or dots.
    function! Duplicate(dstFilename)
      let l:src=expand("%:p")
      let l:dst=expand("%:p:h")."/".a:dstFilename
      let l:cmd="sav! ".l:dst
      execute l:cmd
      redraw!
      echo l:src." duplicated to ".l:dst
    endfunction
    
    " :Duplicate foo.txt
    command! -nargs=1 Duplicate call Duplicate()
    
    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Copyright © 2023 · Bootstrap4 Genesis on Genesis Framework · WordPress · Log in