0

I am trying to create a batch script to move files base on search criteria into another folder of the same subfolder structure.

I tried the following but the result is not quite right.

for /r "c:\Test_Copy\Source1\" %%x in (Test*.txt) do move "%%x" "c:\Test_Copy\Target1\"

As it is showing

move "c:\Test_Copy\Source1\Sub1\Test1.txt" "c:\Test_Copy\Target1\"
move "c:\Test_Copy\Source1\Sub2\Test1.txt" "c:\Test_Copy\Target1\"

I would like the outcome to be the following.

move "c:\Test_Copy\Source1\Sub1\Test1.txt" "c:\Test_Copy\Target1\Sub1\Test1.txt"
move "c:\Test_Copy\Source1\Sub2\Test1.txt" "c:\Test_Copy\Target1\Sub2\Test1.txt"

How will I be able to achieve this?

Thanks

2
  • 2
    Open a command prompt, run robocopy /? and read the output help. The solution is: %SystemRoot%\System32\robocopy.exe "C:\Test_Copy\Source1" "C:\Test_Copy\Target1" Test*.txt /S /MOVE /NDL /NFL /NJH /NJS. ROBOCOPY does not only move the matching files with replicating the directory structure in target directory, it deletes also source directories being empty after file movement. The empty source directories can be kept with using /MOV instead of /MOVE. Note: Source and target directory paths must be without \ (or with \\) at end! Commented Jun 8, 2020 at 5:38
  • ROBOCOPY interprets \ as escape character if it is left to one more backslash or to a double quote which is an unusual behavior. \ is interpreted as literal character if left to any other character than \ or ". That is the reason why source and target directory paths should have never \ at end on using ROBOCOPY as a single backslash at end of the paths would escape " and ROBOCOPY would interpret all other characters up to next " as part of source/target directory name although a " is not valid inside a source/directory path. Commented Jun 8, 2020 at 5:44

3 Answers 3

1

The for /R loop returns full absolute paths, even the ~-modifiers do not allow to return relative paths. However, you could use xcopy /L, which just lists files that it would copy without the /L option, with paths relative to the source root directory; that list can easily be captured and processed by a for /F loop:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_SOURCE=C:\Test_Copy\Source1"
set "_DESTIN=C:\Test_Copy\Target1"

rem // Change into source root directory:
pushd "%_SOURCE%" && (
    rem /* Run `xcopy /L` to list files but not actually copy any, because this returns
    rem    paths relative to the source root directory; then let `for /F` capture that
    rem    list, split off the preceding drive letter and skip the summary line: */
    for /F "tokens=2 delims=:" %%F in ('
        xcopy /L /S /Y "Test*.txt" "%TEMP%"
    ') do (
        rem // Create potentially needed sub-directory, suppress errors when it exists:
        2> nul md "%_DESTIN%\%%F\.."
        rem // Actually move the currently iterated file into the destination directory:
        move "%%F" "%_DESTIN%\%%F"
    )
    rem // Return from source root directory:
    popd
)

endlocal
exit /B

The great advantage of this method is that no string manipulation is involved to derive relative paths, which is dangerous as it could fail in certain situations (for instance, when a root path like D:\ is given, or when a path like D:\some\.\source\dummy\..\folder is specified).


Of course you can also use robocopy /L as suggested in a comment by user Mofi:

robocopy "C:\Test_Copy\Source1" "C:\Test_Copy\Target1" "Test*.txt" /S /MOV /NDL /NJH /NJS
Sign up to request clarification or add additional context in comments.

Comments

1
@echo off 

for /D /R "c:\Test_Copy\Source1\" %%I in (*)do pushd "%%~I" & (
    2>nul >nul mkdir "c:\Test_Copy\Target1\%%~nxI" 
    move "%%~I\Test*.txt" "c:\Test_Copy\Target1\%%~nxI" & popd
   )
  • Outputs command loop results:
move "c:\Test_Copy\Source1\Sub1\test_001.txt" "c:\Test_Copy\Target1\Sub1\test_001.txt"
move "c:\Test_Copy\Source1\Sub1\test_002.txt" "c:\Test_Copy\Target1\Sub1\test_002.txt"
move "c:\Test_Copy\Source1\Sub1\test_003.txt" "c:\Test_Copy\Target1\Sub1\test_003.txt"
move "c:\Test_Copy\Source1\Sub2\test_001.txt" "c:\Test_Copy\Target1\Sub2\test_001.txt"
move "c:\Test_Copy\Source1\Sub2\test_002.txt" "c:\Test_Copy\Target1\Sub2\test_002.txt"
move "c:\Test_Copy\Source1\Sub2\test_003.txt" "c:\Test_Copy\Target1\Sub2\test_003.txt"
  • The move command results:
c:\Test_Copy\Source1\Sub1\test_001.txt
c:\Test_Copy\Source1\Sub1\test_002.txt
c:\Test_Copy\Source1\Sub1\test_003.txt
        3 file(s) moved.
c:\Test_Copy\Source1\Sub2\test_001.txt
c:\Test_Copy\Source1\Sub2\test_002.txt
c:\Test_Copy\Source1\Sub2\test_003.txt
        3 file(s) moved.

Obs.: If all your subfolders c:\Test_Copy\Target1\Sub[n] already exist, remove line 2>nul >nul mkdir "c:\Test_Copy\Target1\%%~nxI"

@echo off 

For /D /R "c:\Test_Copy\Source1\" %%I in (*)do pushd "%%~I" & (
    move "%%~I\Test*.txt" "c:\Test_Copy\Target1\%%~nxI" & popd
   )
  • Try using for /D /R:
FOR /R - Loop through files (recursively)
FOR /D - Loop through several folders/directories

The option /D /R is undocumented, but can be a useful combination, while it will recurse through all subfolders the wildcard will only match against Folder/Directory names (not filenames)

Source linked to ss64.com

Rem :: Set your folder /Directory /Recursively tree starting at "c:\Test_Copy\Source1"
For /D /R "c:\Test_Copy\Source1"

Comments

0

If you want to do it with batch you need to modify the value of %%x to point to the target directory INSIDE the loop. When you do this you can NOT use % to surround the variables you create inside the for loop - you have to use delayed expansion with ! to surround those variables. Then you can use variable replacement on %%x to change it's value.

Like the comments say, this doesn't work if your directory/file names contain more than one exclamation point.

This does what you want I think:

@echo off
setlocal enabledelayedexpansion
set sourcedir=c:\Test_Copy\Source1\
set targetdir=c:\Test_Copy\Target1\
for /r "%sourcedir%" %%x in (Test*.txt) do (
    set sourcefile=%%x
    set destfile=!sourcefile:%sourcedir%=%targetdir%!
    echo move !sourcefile! !destfile!
)

Just change echo move to move when you are ready to actually do the move.

4 Comments

That is a good solution for Windows XP on which ROBOCOPY is not available by default with three disadvantages: The target directory structure must exist already (or md is additionally used inside the loop) and it does not work for files in source directory tree with one or more ! somewhere in full qualified file name because of enabled delayed environment variable expansion and source directory path must not contain = or ! and target directory path not !.
Please test the following. Create a dir called sub3! and a file called Test1!.txt then run this script again :)
@GerhardBarnard Good point. That makes it trickier - maybe I could escape the exclamation mark with a colon or something else that is invalid in a filename and then unescape it later. Sounds terrible. Other than not using a batch solution is there a good way to make it more bullet proof?
Yes, you can use robocopy or not use delayedexpansion

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.