Cp (copy file) function force keyword gives you too much power


#1

With great power comes great responsibility
– some guy

I recently got myself into big trouble using the force keyword. the documentation is clear, it will delete the destination. My misunderstanding was that the destination would be my source file name, i.e. it would overwrite the file if i was trying to copy over a previous version - i made the mistake of assuming it worked like unix cp, i.e. force meant it would simply overwrite the target with the same name to make updating a file with a new copy easier.

but the documentation is clear, it will DELETE THE DESTINATION. my destination was a directory full of data, and i was trying to copy a file to it. sure, i did a very dumb thing, and sure it’s convenient, but it’s a trap.

How about :
1 removing it - force the user to remove the file/directory explicitly. slightly more inconvenient but would force the user to apply a little extra thought to the process.

2 makes it’s operation more unix cp like, i.e. it will only remove something that has the same name as the source, i.e. cp(“somefile.txt”, “somedir”, force=true) will overwrite somefile.txt but won’t delete somedir.

yes, all my fault. yes it’s convenient, but I can hardly believe i’m the first person to have fallen into this trap.

just as reference neither python’s shutil copy or copyfile allow such a thing.

Thanks,


#2

The purpose of the force argument in cp & friends is precisely that it allows to skip checking existing files — it will succeed if you have write permissions and the disk is not full.

Failing for directories would defeat the purpose. As would imitating Unix’s cp: if I did

cp(src, dest; force = true)

and it does not error, I can reasonably expect that dest now has the contents I intended. Your suggestion would silently place it at joinpath(src, dest).


#4

A major difference between the cp of most shells and Julias cp function is that in a shell

cp file dir

will copy the file “file” into the directory “dir” (if “dir” exists and is a directory.)

In Julia, on the other hand

cp("file", "dir")

will throw an ArgumentError if there’s a directory named “dir”.

Maybe one could introduce a new kwarg and change that error to: “ArgumentError: ‘dir’ exists and is a directory. Use into=true to copy ‘file’ into ‘dir’, or use force=true to remove ‘dir’ before copying.”


#5

i’ve been thinking about this, wondering why nobody else thinks that a flag set to true causing the deletion of an entire directory is not a problem.

but instead of complaining some more, here’s my suggestion:

copyfile(src, dest, force::Boolean)

the semantics are obvious ( i think), but i’ll spell it out anyway

src must be a filename.
dest can be a directory or a filename

if dest is a directory, the file is copied to the directory.
if dest is a filename, the file is copied to the new filename.

so the actual destination filename is either dest or src/dest

force=true causes the destination FILE to be deleted if it exists.

force=false throws an error if the destination FILE exists.

in fact, an update flag might be a nice-to-have also, so that the destination file isn’t overwritten unless src is newer.

I’m going to write it up for my own use. happy to contribute it to the filesystem module :slight_smile: