Cut, Copy, and Paste Files from the Command Line

I prefer to work from the command line, often switching between many terminals via something like tmux. I think it would be convenient to be able to cut, copy, and paste files between the current working directories (CWDs) in different terminals without having to write out the relative paths. For example, if the CWD of my terminal is /a/b/c/ and I want to copy a file called foo.txt to directory /d/e/f/, then I have to write

cp foo.txt ../../../d/e/f

Analogously, to move the file without copying (analogous to the "cut" verb of GUIs), I'd write

mv foo.txt ../../../d/e/f

Writing out the whole relative path ../../../d/e/f is pretty annoying (did I get the number of .. right?), especially if I already have another terminal open with CWD /d/e/f/. I would prefer to be able to somehow cut or copy the file in one terminal then paste it in another, similar to how GUI file managers typically work.

A while ago I wrote a Python tool to provide this functionality by copying files to and from an intermediate directory. However, I no longer use this tool because there are simpler methods, two of which I'll discuss here.

xclip

Probably the simplest method of all is to just use xclip, which comes with the commands

xclip-copyfile
xclip-cutfile
xclip-pastefile

which do exactly what we want. For convenience, I'd probably alias these to something shorter like

alias xcp=xclip-copyfile
alias xmv=xclip-cutfile
alias pst=xclip-pastefile

The names xcp and xmv are inspired both by x in xclip as well the great renameutils package, which provides the quick (qcp, qmv) and interactive (icp, imv) variants of cp and mv. These commands can be quite handy, so I'd recommend checking out renameutils if you haven't before.

One thing I don't like about this solution is that once a file has been cut using xclip-cutfile, it's stuck in the mysterious xclip clipboard. If I accidentally copy or cut something else before I paste it, then it's gone. Another (smaller) downside is that the file in the clipboard can only be pasted once, when I might like to copy the same file to multiple destinations.

A simple shell script

In an attempt to remedy the drawbacks of the xclip solution, I wrote a simple alternative shell script. Instead of the traditional order of cut or copy, then paste, I actually felt it to be more elegant to "mark" a set of files and then choose whether they should be copied or moved to the new location. Before I explain further, here is the script itself (you can also find it here):

CLIPBOARD_FILE=~/.xcp-clipboard

# mark the file(s) for later use with cp or mv
xmark() {
  realpath "$@" > "$CLIPBOARD_FILE"
}

# copy the marked files
xcp() {
  local files
  while read line; do
    files+=("$line")
  done < "$CLIPBOARD_FILE"
  cp $files $@
}

# move the marked files
xmv() {
  local files
  while read line; do
    files+=("$line")
  done < "$CLIPBOARD_FILE"
  mv $files $@
}

To access these commands, you just need to copy the above code to your shell's rc file (like the .bashrc) or otherwise source it.

Let's return to our example from the beginning of the post to see how these commands work. We have two terminal windows: Terminal #1 has CWD /a/b/c/ and Terminal #2 has CWD /d/e/f/. The goal is to copy foo.txt from /a/b/c/ to /d/e/f/ without writing tedious relative paths. Making use of the functions in the above script, in Terminal #1 I can run:

xmark foo.txt

which just "marks" the file by writing its full path /a/b/c/foo.txt to the clipboard file. Now, in Terminal #2, I can just run:

xcp .

and the file will be copied from its original location to the CWD /d/e/f/. Alternatively I could use xmv to move the file (i.e., deleting the original).

Looking at the definitions of the xcp and xmv functions above, one can see that they actually just call cp and mv respectively, except that the paths listed in the clipboard file are added as the first arguments. Any arguments supplied to xmv or xcp get passed along directly to mv or cp, respectively, which means we get to leverage all of the power of mv and cp for free. Want the command to print out what is being done? Pass -v (verbose). Want to avoid overwriting an existing file? Use -n (no-clobber). I was surprised by how many options there were when I read the man pages for for cp and mv: there is a lot going on there!

And that's it: as you can see, it is actually quite simple to cut and copy files from the command line. As usual, I'm interested in hearing about alternative solutions to the problem, as well as comments on the ones proposed above. The best way to reach me is by email.