DextrousDave
DextrousDave

Reputation: 6793

batch script loop through folders, zip each file in the folder and move it to a corresponding folder in a different location

I have two sets of folders:

"C:\temp\eFS_manual_temp" contains a set of subfolders. Another directory, "C:\temp\eFS_manual\eFS", contains the same set of subfolders.

I want to use the below batch script to zip all files in the subfolders, one folder at a time, and move the zip files to to the secondary location with the same folder structure:

SET SourceDir=C:\temp\eFS_manual\_temp
SET DestDir=C:\temp\eFS_manual\eFS

for /D %%a in ("%SourceDir%") do (

    CD /D "C:\Program Files\WinRAR"
    FOR /F "TOKENS=*" %%F IN ('DIR /B /A-D "%SourceDir%\%%a"') DO (
        RAR.exe a -ep "%DestDir%\%%a\%%~NF.zip" "%SourceDir%\%%a\%%~NXF"
    )

)

I get the following error: filename, directory name, or volume label syntax is incorrect.

Upvotes: 1

Views: 5135

Answers (2)

Mofi
Mofi

Reputation: 49086

The few lines of batch file code contains several mistakes.

The first one is on line for /D %%a in ("%SourceDir%") do. The option /D is ignored by FOR on a set in round brackets not containing any wildcard. This can be seen easily on opening a command prompt window and run:

for /D %I in ("%SystemRoot%") do @echo %I

This command line just prints "C:\Windows" which is exactly the string in parenthesis.

So let us run this command line with a wildcard appended:

for /D %I in ("%SystemRoot%\*") do @echo %I

The output is much better because of listed are all non-hidden subdirectories in Windows system directory.

This is the main mistake result in the error message on execution of the batch file in combination with the other errors as described below.

There are multiple mistakes on command line

FOR /F "TOKENS=*" %%F IN ('DIR /B /A-D "%SourceDir%\%%a"') DO

%%a would expand to full qualified name of a subdirectory in %SourceDir% in outer FOR command line would be correct. FOR assigns to specified loop variable just name of non-hidden directory on set in round brackets contains no path, i.e. just for /D %I in (*) do @echo %I would be used to process the non-hidden subdirectories in current directory. But on set in parenthesis containing an absolute or relative path, FOR assigns to specified loop variable the directory name with the specified absolute or relative path. This behavior can be seen on running in a command prompt window following commands:

cd /D %SystemDrive%\
for /D %I in (.\*) do @echo %I
for /D %I in (%SystemDrive%\*) do @echo %I

RAR.exe creates an entire directory tree to a specified destination directory automatically on extraction of an archive. But the destination directory must already exist on creation of an archive. So it is advisable to make sure in destination directory that the current subdirectory from source directory exists also in destination directory.

FOR executes the embedded DIR command line in a separate command process started with cmd.exe /C in background and captures all lines output to handle STDOUT of this separate command process by DIR. DIR outputs in this case just file names with file extension of all files including files with hidden attribute set found in specified directory.

Captured empty lines are skipped by FOR, as well as lines starting with ; because of eol=; is the default. tokens=* results in removing leading spaces/tabs from a captured line and if there is something left, the remaining line is assigned to specified loop variable for being processed by the commands in body of FOR loop. That behavior is not good in case of a file name starts with a space or a semicolon. Better would be usage of delims= to turn off line splitting behavior completely and get assigned complete file name to specified loop variable.

But even on fixing all those errors, there would be still a problem. The manual for console application Rar.exe is the text file Rar.txt in WinRAR program files folder. It clearly explains at top that Rar.exe supports only RAR archives. It does not support other archive types like ZIP as WinRAR.exe supports. So Rar.exe cannot be used for creating ZIP archive files.

Examples makes the developers life easier and so let us start with an example:

The source directory C:\temp\eFS_manual\_temp contains following directories and files:

  • Subfolder 1 -    File 1.txt - ;Comment & report! it.readme - File 2.csv
  • Subfolder 2 - .htaccess - One more % file.txt

That are really ugly file names for processing with a batch file because of

  • file name starting with a space;
  • file name starting with a semicolon;
  • file name containing an exclamation mark;
  • file name containing a percent sign;
  • file name starting with a dot and having no more dot.

None of the files and directories has the hidden attribute set.

The destination directory C:\temp\eFS_manual\eFS should contain after batch file execution nevertheless:

  • Subfolder 1 -    File 1.zip - ;Comment & report! it.zip - File 2.zip
  • Subfolder 2 - .htaccess.zip - One more % file.zip

And the ZIP files are of course really ZIP compressed archive files containing each just one compressed file without path.

This goal can be achieved with:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\temp\eFS_manual\_temp"
set "DestDir=C:\temp\eFS_manual\eFS"

for /D %%I in ("%SourceDir%\*") do (
    md "%DestDir%\%%~nxI" 2>nul
    for %%J in ("%%I\*") do (
        if not "%%~nJ" == "" (
            "%ProgramFiles%\WinRAR\WinRAR.exe" a -afzip -cfg- -ep -ibck -inul -m5 -y -- "%DestDir%\%%~nxI\%%~nJ.zip" "%%J"
        ) else (
            "%ProgramFiles%\WinRAR\WinRAR.exe" a -afzip -cfg- -ep -ibck -inul -m5 -y -- "%DestDir%\%%~nxI\%%~xJ.zip" "%%J"
        )
    )
)

endlocal

It can be seen on how this batch file works by changing first line to @echo on and running the batch file from within a command prompt window as in this case Windows command processor outputs each line after preprocessing/parsing as described at How does the Windows Command Interpreter (CMD.EXE) parse scripts? before execution of the command line.

It would be necessary to use command DIR if there are also directories or files in source directory with hidden attribute set which should be also processed by this batch file.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\temp\eFS_manual\_temp"
set "DestDir=C:\temp\eFS_manual\eFS"

for /F "eol=| delims=" %%I in ('dir "%SourceDir%\*" /AD /B 2^>nul') do (
    md "%DestDir%\%%I" 2>nul
    for /F "eol=| delims=" %%J in ('dir "%SourceDir%\%%I\*" /A-D /B 2^>nul') do (
        if not "%%~nJ" == "" (
            "%ProgramFiles%\WinRAR\WinRAR.exe" a -afzip -cfg- -ep -ibck -inul -m5 -y -- "%DestDir%\%%I\%%~nJ.zip" "%SourceDir%\%%I\%%J"
        ) else (
            "%ProgramFiles%\WinRAR\WinRAR.exe" a -afzip -cfg- -ep -ibck -inul -m5 -y -- "%DestDir%\%%I\%%~xJ.zip" "%SourceDir%\%%I\%%J"
        )
    )
)

endlocal

This batch file takes into account that DIR with option /B and without option /S outputs directory and file names always without path.

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

The WinRAR command line is created by

  • starting WinRAR,
  • opening from last menu Help the help by clicking on first menu item Help topics,
  • navigating on tab Contents to list item Commmand line mode,
  • expanding this list item with a click on [+],
  • clicking on sublist item Command line syntax and copying bold line at top as template for the command line,
  • expanding left in list with a click on [+] the sublist item Commands and clicking on Alphabetic commands list and choose a as the command to use for this task,
  • expanding left in list with a click on [+] the sublist item Switches and clicking on Alphabetic switches list and choose the switches suitable for this task,
  • and finally use the right strings for archive file name and file name to archive.

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.

  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • if /?
  • md /?
  • set /?
  • setlocal /?

Upvotes: 3

lit
lit

Reputation: 16236

The trickiest part of this for me was getting the new directory path built into $NewDir. Once that was done, using Compress-Archive to make a .zip format archive was easy. This assumes you want to create empty directories if the source directory contained no files.

Since you wanted a .zip archive file, I used Compress-Archive. If you want to use another tool (rar.exe or 7z.exe) that could be used instead with some changes.

$SourceDir = 'C:\src\t'
$DestDir = 'C:\src\tarch'

Get-ChildItem -Recurse -Directory -Path $SourceDir |
    ForEach-Object {
        $SubName = $_.FullName.Replace($SourceDir, '')
        $NewDir = $DestDir + $SubName
        if (-not (Test-Path -Path $NewDir)) { New-Item -ItemType Directory -Path $NewDir }

        $files = Get-ChildItem -File -Path $_.FullName
        if ($files.Count -gt 0) {
            $files | Compress-Archive -DestinationPath $NewDir\archcopy.zip
        }
    }

To run this in a .bat file script, put the code above into a file such as Archive-eFS_manual.ps1. Run it using:

powershell -NoProfile -File .\Archive-eFS_manual.ps1

Upvotes: 0

Related Questions