Robust cross-platform method of moving a directory
Asked Answered
C

2

6

What is the most robust method to move an entire directory from say /tmp/RtmpK4k1Ju/oldname to /home/jeroen/newname? The easiest way is file.rename however this doesn't always work, for example when from and to are on different disks. In that case the entire directory needs to be recursively copied.

Here is something I came up with, however it's a bit involved, and I'm not sure it will work cross-platform. Is there a better way?

dir.move <- function(from, to){
  stopifnot(!file.exists(to));
  if(file.rename(from, to)){
    return(TRUE)
  }
  stopifnot(dir.create(to, recursive=TRUE));
  setwd(from)
  if(all(file.copy(list.files(all.files=TRUE, include.dirs=TRUE), to, recursive=TRUE))){
    #success!
    unlink(from, recursive=TRUE);
    return(TRUE)
  }
  #fail!
  unlink(to, recursive=TRUE);
  stop("Failed to move ", from, " to ", to);
}
Curley answered 27/7, 2013 at 10:43 Comment(1)
I am not entirely sure, what your actual question is. To me your code looks fine, but I would never declare something cross-platform without knowing all platforms we are talking about and testing the code on all of them. So do you want us to test your code on different platforms reporting the outcome? If not, I guess your code is quite straight-forward and not involved at all considering what your're up to. The only less involved solution I see was given by Carl Witthoft (including the improvements suggested in his comment) but it seems that is not what you are looking for.Shortie
L
3

I think file.copy shall be sufficient.

file.copy(from, to, overwrite = recursive, recursive = FALSE,
          copy.mode = TRUE)

From ?file.copy:

from, to: character vectors, containing file names or paths.  For
         ‘file.copy’ and ‘file.symlink’ ‘to’ can alternatively
         be the path to a single existing directory.

and:

recursive: logical.  If ‘to’ is a directory, should directories in
          ‘from’ be copied (and their contents)?  (Like ‘cp -R’ on
          POSIX OSes.)

From the description about recursive we know from can have directories. Therefore in your above code listing all files before copy is unnecessary. And just remember the to directory would be the parent of the copied from. For example, after file.copy("dir_a/", "new_dir/", recursive = T), there'd be a dir_a under new_dir.

Your code have done the deletion part pretty well. unlink has a nice recursive option, which file.remove doesn't.

unlink(x, recursive = FALSE, force = FALSE)
Lanai answered 30/7, 2013 at 18:41 Comment(6)
... followed by a removal of the original.Expulsion
Just thought the original code had done well on that part. :DLanai
The thing I don't like about file.copy is that it to can either be interpreted as copy as or copy in this dir. And this behavior then changes by OS and depending on if to is an existing directory.Curley
@Jeroen, glad to know to may behave copy as or copy in. Here is a link to the c source code on GitHub, and you can locate the function by searching int do_copy, which is called by do_filecopy, the .Internal version of file.copy. It seems one version of do_copy handles Win32 and the other for the rest. If this is the origin of the copy as vs. copy in problem, then I suggest you treat platforms separately likewise, i.e obtain platform info and do different things.Lanai
Well there is also ambiguity on the OS level between mv foo bar and mv foo bar/Curley
@Jeroen, I think we are discussing providing a functionality for some bigger purpose, i.e. analysis, reporting, or archive, etc. Once a code works consistently across platforms, it is cross-platform. You don't have to simulate file operations with R to a level that behave like native commands on each platform.Lanai
K
0

Why not just invoke the system directly:

> system('mv /tmp/RtmpK4k1Ju/oldname /home/jeroen/newname')  
Krenek answered 27/7, 2013 at 11:29 Comment(5)
That's not really cross-platform is it?Darwin
In addition, I would like to minimize depending on external programs that require additional privileges.Curley
@Jeroen you can NEVER delete files w/out having the underlying privilege level. @spacedman - it's certainly cross-platform for Unix, Linux, OSX, and anyone who's loaded cygwin. And for goodness' sake, it's not that hard to write an if/else structure which looks up the current type of OS and makes the appropriate call ('mv' vs. whatever the septic-vomit-pile that is CommandPrompt uses to move files around). I stand by my position that it's easier to use system calls than create some fancy workaround inside R.Krenek
I really don't think you can claim this is cross-platform unless you at least follow your suggestion and write it as if (.Platform$OS.type=="unix") system("mv ...") else ... and what's in the second ... is likely to look a lot like what Jeroen already proposed ... Like it or not, there are a lot of Windows systems out there!Mcfadden
Windows does have a move command, but it's cleaner all round just to copy-and-remove as per Icn's answer.Ignore

© 2022 - 2024 — McMap. All rights reserved.