Winson Lee
Winson Lee

Reputation: 17

Batch file with a recursive for loop to move file

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

Upvotes: 0

Views: 2952

Answers (3)

Jerry Jeremiah
Jerry Jeremiah

Reputation: 9618

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.

Upvotes: 0

Io-oI
Io-oI

Reputation: 2565

@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"

Upvotes: 1

aschipfl
aschipfl

Reputation: 34929

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

Upvotes: 1

Related Questions