Reputation: 6793
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
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:
That are really ugly file names for processing with a batch file because of
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:
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
a
as the command to use for this task,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
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