It seems mv -T is a GNU extension to mv.

Is there a robust way (race-free, portable, and otherwise without "gotchas") to do the equivalent of mv -T dir1 dir2?

To be clear:

  • I DO NOT want this to ever result in dir2/dir1. If dir2 exists, I want the command to fail. If dir1 is moved at all, it must become dir2.

  • I DO NOT want to move out every child one-by-one. I want to move the directory itself.

  • I DO want to avoid race conditions. It's trivial to test if dir2 exists first, but then it might be created after the check but before the move.

  • I don't think it's possible to satisfy "If dir2 exists, I want the command to fail" portably; POSIX requires rename to delete an empty dir2 and succeed. – Michael Homer Feb 26 '19 at 3:37
  • The spec text: "If the old argument points to the pathname of a directory, the new argument shall not point to the pathname of a file that is not a directory. If the directory named by the new argument exists, it shall be removed and old renamed to new. [...] If new names an existing directory, it shall be required to be an empty directory.". That also applies to renameat. I don't believe there can be any portable method to rename a file that does not go through that path. Given that, which of your requirements can you relax? What sorts of race condition are you concerned with? – Michael Homer Feb 26 '19 at 3:41
  • Related: mv: Move file only if destination does not exist – Freddy Feb 26 '19 at 3:51
  • @MichaelHomer: Interesting, thanks. The race condition I'm trying to avoid is dir1 landing inside dir2, which can happen if multiple processes try to run this command at once. However, I guess it would be fine if an empty dir2 were deleted. Would that help? Also, if there's still no single POSIX solution, I'd be interested in knowing if there are even platform-specific solution that I can call. E.g. is there a solution that works on Mac, let alone other platforms? I haven't found any. – user541686 Feb 26 '19 at 3:54
  • The renameat2 system call takes a flag argument and one such flag is RENAME_NOREPLACE which causes it to: "Don't overwrite newpath of the rename. Return an error if newpath already exists." ... But that doesn't really satisfy "portable", so not sure it solves your problem... – filbranden Feb 26 '19 at 4:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.