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.