Kai
Kai

Reputation: 13

How do I copy files with adding folder name to destination file name?

I need to loop through a list of sub folders to copy all files from those sub folders into new folder. I would like to have named the copied files in destination folder with sub folder name + - + file name because multiple sub folders could contain files with same name.

For example, I have the following files:

C:\Old\Folder1\a.txt
C:\Old\Folder1\b.txt
C:\Old\Folder2\a.txt
C:\Old\Folder2\b.txt

I would like to copy the above files to new folder C:\New. The final result should be:

C:\New\Folder1-a.txt
C:\New\Folder1-b.txt
C:\New\Folder2-a.txt
C:\New\Folder2-b.txt

I tried the following code, but it is not working as expected.

for /r "C:\Old" %%d in (*) do copy "%%d" "C:\New\%%~nxI-%%~nxf"

How to copy files with adding folder name to destination file name?

Upvotes: 1

Views: 3246

Answers (3)

Mofi
Mofi

Reputation: 49086

The first batch file works even with one or more exclamation marks ! in any file or folder name.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=%~1"
if not defined SourceFolder set "SourceFolder=C:\Old"
set "SourceFolder=%SourceFolder:/=\%"
if not "%SourceFolder:~-1%" == "\" set "SourceFolder=%SourceFolder%\"
set "TargetFolder=%~2"
if not defined TargetFolder set "TargetFolder=C:\New\"
set "TargetFolder=%TargetFolder:/=\%"
if not "%TargetFolder:~-1%" == "\" set "TargetFolder=%TargetFolder%\"

set "TargetCreated="
if not exist "%TargetFolder%" (
    md "%TargetFolder%" 2>nul
    if not exist "%TargetFolder%" (
        echo ERROR: Failed to create destination folder:
        echo/
        echo "%TargetFolder%"
        echo/
        pause
        goto EndBatch
    )
    set "TargetCreated=yes"
)

for /F "delims=" %%I in ('dir "%SourceFolder%*" /A-D-H /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /L /V /C:"%TargetFolder:\=\\%"') do call :FileCopy "%%I"

if defined TargetCreated rd "%TargetFolder%" 2>nul
goto EndBatch

:FileCopy
set "FilePath=%~dp1"
set "FilePath=%FilePath:~0,-1%"
for %%J in ("%FilePath%") do set "FolderName=%%~nxJ-"
if "%FilePath:~-1%" == ":" set "FolderName="
copy /Y %1 "%TargetFolder%%FolderName%%~nx1" >nul
goto :EOF

:EndBatch
endlocal

The disadvantage is that this batch file is slower on copying thousands of files in comparison to second batch file below.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFolder=%~1"
if not defined SourceFolder set "SourceFolder=C:\Old"
set "SourceFolder=%SourceFolder:/=\%"
if not "%SourceFolder:~-1%" == "\" set "SourceFolder=%SourceFolder%\"
set "TargetFolder=%~2"
if not defined TargetFolder set "TargetFolder=C:\New\"
set "TargetFolder=%TargetFolder:/=\%"
if not "%TargetFolder:~-1%" == "\" set "TargetFolder=%TargetFolder%\"

set "TargetCreated="
if not exist "%TargetFolder%" (
    md "%TargetFolder%" 2>nul
    if not exist "%TargetFolder%" (
        echo ERROR: Failed to create destination folder:
        echo/
        echo "%TargetFolder%"
        echo/
        pause
        goto EndBatch
    )
    set "TargetCreated=yes"
)

setlocal EnableDelayedExpansion
for /F "delims=" %%I in ('dir "%SourceFolder%*" /A-D-H /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /L /V /C:"%TargetFolder:\=\\%"') do (
    set "FilePath=%%~dpI"
    set "FilePath=!FilePath:~0,-1!"
    for %%J in ("!FilePath!") do set "FolderName=%%~nxJ-"
    if "!FilePath:~-1!" == ":" set "FolderName="
    copy /Y "%%I" "%TargetFolder%!FolderName!%%~nxI" >nul
)
endlocal

if defined TargetCreated rd "%TargetFolder%" 2>nul

:EndBatch
endlocal

Both batch files can be started without any argument, with just one argument being interpreted as source folder path or with two arguments on which second argument is interpreted as target folder path. C:\Old is defined as source folder path if batch file is started without any argument, C:\New is defined as target folder path if batch file is started without a second argument. The batch files make sure that both folder paths end with a backslash.

The target folder is created if not existing already with verifying successful creation of the target folder. An error message is output and execution of batch file is halted before exiting batch file execution on destination folder could not be created successfully.

It is possible that destination folder is a subfolder of source folder. For that reason a command line with DIR and FINDSTR is executed by FOR in a separate command process executed in background started with %ComSpec% /c and the specified command line appended to get all file names to copy with filtering out all file names in source folder tree starting with folder path being equal target folder path. So executed by FOR is for example:

C:\Windows\System32\cmd.exe /c dir "C:\Old\*" /A-D-H /B /S 2>nul | C:\Windows\System32\findstr.exe /B /L /V /C:"%C:\\New\\"

DIR searches

  • in specified directory C:\Old and all its subdirectories because of option /S
  • for non-hidden files because of option /A-D-H (attribute not directory and not hidden)
  • matching the wildcard pattern * (any file name) and
  • outputs found in bare format just the file names because of option /B
  • with full path because of option /S.

This output by DIR is redirected by started cmd.exe in background to FINDSTR which

  • searches just at beginning of all lines because of option /B
  • for a literal interpreted string explicitly specified with option /L
  • for search string specified with option /C: on which each backslash is escaped with one more backslash
  • and outputs inverted result because of option /V which means all lines NOT starting with the search string.

Note: A source folder like C:\Old\Temp and a destination folder C:\Old results in not copying any file. In other words the destination folder can be a subfolder of source folder, but the source folder cannot be a subfolder of destination folder.

The output of FINDSTR to handle STDOUT (standard output) of started command process is captured by FOR and processed line by line after started cmd.exe terminated itself.

Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.

FOR with option /F ignores by default all empty lines which do not occur here.

FOR with option /F would split up by default each line into substrings using normal space and horizontal tab as string delimiters and would assign just first space/tab delimited string to specified loop variable I. A file/folder name can contain one or more spaces. For that reason option delims= is specified in double quotes to define an empty list of delimiters which turns off line splitting completely to get assigned each full qualified file name completely to loop variable I.

FOR with option /F ignores by default also all lines starting with a semicolon after removing the delimiters (here none) at beginning of each line because of eol=; is the default for end of line option. But file names with full path always start either with a drive letter and a colon or with two backslashes in case of specified source folder path is a UNC path. So it is not necessary to change default end of line option.

The first batch file calls subroutine FileCopy with current full qualified file name as first and only argument to avoid the usage of delayed expansion.

The second batch file uses delayed environment variable expansion which is faster, but has the disadvantage that an exclamation mark in a folder or file name is interpreted as begin/end of a delayed expanded environment variable reference and so everything between two ! is replaced by value of the referenced environment variable or nothing if no environment variable exists with such a name and a single ! is simply removed from file/folder string.

The full path of the file is assigned to an environment variable FilePath. The file path ends always with a backslash which must be removed before the folder name can be determined by using one more FOR assigned to FolderName. %%~nxJ references everything after last backslash which is usually file name and file extension, but in this case the name of the folder containing the file. The hyphen is appended already to folder name.

Note: Files in root folder of a drive specified as source folder like C:\ are copied by this batch file with destination file name being source file name.

The current file is copied with destination file name being source file name with folder name and a hyphen inserted at beginning, except for files in root directory of a drive. There is done no verification on successful file copying by this batch file.

The batch files remove the target folder on being created before and target folder is still empty because no file to copy found in source folder tree.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • copy /?
  • dir /?
  • echo /?
  • endlocal /?
  • findstr /?
  • goto /?
  • if /?
  • md /?
  • pause /?
  • rd /?
  • set /?
  • setlocal /?

See also:

Upvotes: 1

aschipfl
aschipfl

Reputation: 34899

Since you have got a certain hierarchy depth in your source directory let me recommend not to use for /R, but for /D to enumerate directories and for to enumerate files:

@echo off
rem // Iterate through the immediate sub-directories of the source directory:
for /D %%D in ("C:\Old\*") do (
    rem // Iterate through all files in the currently iterated sub-directory:
    for %%F in ("%%~D\*.*") do (
        rem /* Copy the currently iterated file into the destination directory
        rem    and rename it so that the parent directory name is prefixed: */
        copy /-Y "%%~F" "C:\New\%%~nxD-%%~nxF"
    )
)

Replace /-Y by /Y if you not want to be prompted to overwrite already existing files in the destination directory.

Upvotes: 0

user6811411
user6811411

Reputation:

Not knowing if there are more levels of subfolders (what the usage of for /r hints at)
With only one level of subfolders of C:\OLD things can be much easier:

:: Q:\Test\2019\08\22\SO_57603775.cmd
@Echo off
Set "Src=C:\OLD"
Set "Dst=C:\NEW"

md "%Dst%" >NUL 2>&1 ||(Echo can't create %Dst% ... exiting&pause&Exit /B 1)
cd /d "%Src%"        ||(Echo can't locate %Src% ... exiting&pause&Exit /B 1)

for /D %%D in (*) do for %%F in (%%D\*) do Copy "%%~fF" "%Dst%\%%~nxD-%%~nxF"

Otherwise there is Mofis good and thoroughly explained answer.

Upvotes: 0

Related Questions