7

So I just wrote a quick python script to move some large directories around (all on the same drive), incorrectly assuming windows command line tools weren't a complete joke and that move Root\Dir1 Root\Dir2 would, like windows explorer GUI, merge the contents. I really don't care whether it replaces or skips duplicate files within the folders because there aren't any.

Unfortunately (in an admin command prompt),

C:\>mkdir a

C:\>mkdir b

C:\>mkdir b\a

C:\>move b\a .
Overwrite C:\a? (Yes/No/All): yes
Access is denied.

... :O

... ?? really ??!?

... no, actually really really ???

It seems the only way is to copy and delete. Painfully pathetic.

Related:

I'm not writing code to copy files one by one. Is there any way to achieve a folder move with replace without copying?

I'd prefer to use some native executable if possible. I'd also be quite happy to use python if it supported it.

Community
  • 1
  • 1
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • Have a look at `robocopy`: http://technet.microsoft.com/en-us/library/cc733145.aspx. Here it's used to solve issue similar (I think) to yours: http://superuser.com/questions/606710/merge-directories-without-overwriting-conflicts – wmz Mar 24 '14 at 15:45
  • @wmz thanks, I'll have another look at it although I thought it still performs a copy/delete instead of a move on the same filesystem. – jozxyqk Mar 25 '14 at 16:21

4 Answers4

8

The move-all-files-manually workaround in python. I'm still reeling from the stupidity.

def moveTree(sourceRoot, destRoot):
    if not os.path.exists(destRoot):
        return False
    ok = True
    for path, dirs, files in os.walk(sourceRoot):
        relPath = os.path.relpath(path, sourceRoot)
        destPath = os.path.join(destRoot, relPath)
        if not os.path.exists(destPath):
            os.makedirs(destPath)
        for file in files:
            destFile = os.path.join(destPath, file)
            if os.path.isfile(destFile):
                print "Skipping existing file: " + os.path.join(relPath, file)
                ok = False
                continue
            srcFile = os.path.join(path, file)
            #print "rename", srcFile, destFile
            os.rename(srcFile, destFile)
    for path, dirs, files in os.walk(sourceRoot, False):
        if len(files) == 0 and len(dirs) == 0:
            os.rmdir(path)
    return ok

Please post a proper answer if there ever is one!

jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • 1
    Maybe I'd try to do a single walk bottom up and immediately remove path [like this](https://unix.stackexchange.com/questions/127712/merging-folders-with-mv/510724#510724). Thanks for the code. – Ciro Santilli OurBigBook.com Apr 05 '19 at 14:12
  • @CiroSantilli新疆改造中心996ICU六四事件, i have added jozxyqk's check to your implementation ```if os.path.isfile(destFile): continue``` And also the shutil to remove the dir with all its un-moved (due to already existence) files ```for dirname in dirs: shutil.rmtree(os.path.join(path, dirname))``` – Octav May 22 '19 at 21:14
1

Merge with Overwrite

this altered functiion overwrits files in Target which are duplicated by filename in source and Target. Existing non duplicated files are not touched. Non existing files and folders are copied from Source to Target. Function derived from @jozxyqk.

def merge_overwrite_Tree(sourceRoot, destRoot):
  #https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command
  '''
  Updates destenation root, overwrites existing files.
  :param sourceRoot: Root folder from wehere to copy the files
  :param destRoot: Destination folder where new folders and files are created and new files are added
  :return: !=0 in case of errors
  '''
  if not os.path.exists(destRoot):
    return 1
  ok = 0
  for path, dirs, files in os.walk(sourceRoot):
    relPath = os.path.relpath(path, sourceRoot)
    destPath = os.path.join(destRoot, relPath)
    if not os.path.exists(destPath):
      print("create: %s"%destPath)
      os.makedirs(destPath)
    for file in files:
      destFile = os.path.join(destPath, file)
      if os.path.isfile(destFile):
        print "\n...Will overwrite existing file: " + os.path.join(relPath, file)
        #ok = False
        #continue
      srcFile = os.path.join(path, file)
      # print "rename", srcFile, destFile
      # os.rename(srcFile, destFile) # performs move
      print("copy %s to %s"%(srcFile, destFile))
      shutil.copy(srcFile, destFile) # performs copy&overwrite
  return ok
Cutton Eye
  • 3,207
  • 3
  • 20
  • 39
1

Keeping it batch only your only choice is to use move.

As you already pointed out move is capable of overwriting files but not merging folders. So in order to work around that the script below moves files one at a time. To figure out where files are supposed to go relative to their destination the source folder is stripped from the absolute file path.

Moving (and merging) one folder into another can be done by running call:move "C:\path\source" "C:\path\dest".

Note that this script requires delayed expansion to be disabled.

:: Strip one path from another
::
:: %~1 relative path
:: %~2 absolute path
:strip
set "rec_a_part="
set "rec_a="
set "rec_r_part="
set "rec_r="
for /f "tokens=1,* delims=\" %%t in ("%~2") do (
  set "rec_a_part=%%t"
  set "rec_a=%%u"
)
for /f "tokens=1,* delims=\" %%t in ("%~1") do ( 
  set "rec_r_part=%%t"
  set "rec_r=%%u"
)
if not "!%newp%!"=="!!" set "newp=%newp%\"
if not "!%rec_a_part%!"=="!%rec_r_part%!" set "newp=%newp%%rec_r_part%"
if not "!%rec_r%!"=="!!" call:strip "%rec_r%" "%rec_a%"
goto:eof

:: Internal call handing the move command
::
:: %~1 source dir relative path
:: %~2 source dir absolute path
:: %~3 dest dir
:execute_move
set "newp="
call:strip "%~1" "%~2"
mkdir "%~3\%newp%\.." 2>nul
move /y "%~1" "%~3\%newp%" >nul
goto:eof

:: Moves all files from one folder into another overwriting any existing files
::
:: %~1 source dir
:: %~2 dest dir
:move
for /r "%~1" %%i in (*) do (
  call:execute_move "%%i" "%~1" "%~2"
)
rd /s /q "%~1"
goto:eof

The :strip call was taken from https://stackoverflow.com/a/11925464/13900705 and fixed up to support paths with spaces.

0

See if this works for you.

@echo off'
md "c:\a"
md "c:\b"
type nul > "c:\a\file1.txt"
type nul > "c:\a\file2.txt"
move "c:\a\*.*" "c:\b"
foxidrive
  • 40,353
  • 10
  • 53
  • 68
  • With move * I get `The filename, directory name, or volume label syntax is incorrect`. Also I think it hits the same issue when two subfolders have the same name as well. – jozxyqk Mar 23 '14 at 09:54
  • That snippet works here, even with the typo in the first line. Maybe you have a third party `move` command on your path someplace. – foxidrive Mar 23 '14 at 12:09