The first solution works only on NTFS drives and expects that the batch file is in the directory containing April reports
as subdirectory.
@echo off
for /D %%I in ("%~dp0April reports\??-??-????") do (
for %%J in ("%%I\approved*.pdf") do (
if exist "%%I\un%%~nxJ" del /F "%%I\un%%~nxJ"
if exist "%%I\%%~nJ_V*.pdf" ( del "%%J" ) else ren "%%J" "%%~nJ_%%~nI.pdf"
)
)
On NTFS drives the result is:
- April reports
- 01-04-2018
- approved123_V2_01-04-2018.pdf
- 02-04-2018
- approved123_02-04-2018.pdf
- 30-04-2018
- approved123_30-04-2018.pdf
But this batch file does not work as expected on FAT32 or exFAT drives because of file April reports\01-04-2018\approved123_V2.pdf
is processed three times resulting in having finally the name approved123_V2_01-04-2018_01-04-2018_01-04-2018.pdf
because of list of file entries in file allocation table matching the pattern approved*.pdf
changes while running the inner loop.
The solution for FAT32 and exFAT drives is using for inner loop a captured list of file names.
@echo off
for /D %%I in ("%~dp0April reports\??-??-????") do (
for /F "delims=" %%J in ('dir "%%I\approved*.pdf" /A-D-H /B 2^>nul') do (
if exist "%%I\un%%~nxJ" del /F "%%I\un%%~nxJ"
if exist "%%I\%%~nJ_V*.pdf" ( del /F "%%I\%%J" ) else ren "%%I\%%J" "%%~nJ_%%~nI.pdf"
)
)
Now the result is the same as with first batch file also on FAT32 and exFAT drives.
The command line dir "%%I\approved*.pdf" /A-D-H /B 2>nul
is executed by FOR with using a separate command process started in background with cmd /C
. DIR outputs just the file names without path which requires to reference the path with %%I
where full qualified file name (file path + file name + file extension) is needed on the other command lines. The file names output by DIR to handle STDOUT of background command process are captured by FOR and then processed line by line. So the changes on file allocation table during running the commands DEL and REN do not matter anymore.
Read also 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.
But let us assume the directory structure is as follows according to question Move files up one folder level:
- April reports
- 01-04-2018
- dayreports
- approved123.pdf
- approved123_V2.pdf
- unapproved123.pdf
- 01-04-2018
- dayreports
- approved123.pdf
- unapproved123.pdf
- 30-04-2018
- dayreports
- approved123.pdf
- unapproved123.pdf
By making a small modification to second batch file the same result can be achieved as posted above.
@echo off
for /D %%I in ("%~dp0April reports\??-??-????") do (
for /F "delims=" %%J in ('dir "%%I\dayreports\approved*.pdf" /A-D-H /B 2^>nul') do (
if exist "%%I\dayreports\un%%~nxJ" del /F "%%I\dayreports\un%%~nxJ"
if exist "%%I\dayreports\%%~nJ_V*.pdf" ( del /F "%%I\dayreports\%%J" ) else move /Y "%%I\dayreports\%%J" "%%I\%%~nJ_%%~nI.pdf" >nul
)
rd "%%I\dayreports" 2>nul
)
dayreports
is additionally added to file paths and command MOVE is used instead of command REN to move the remaining file up one level in folder hierarchy with new file name. Command RD is used to delete the folder dayreports
if being finally empty.
Here is one more variant which simply processes all approved*.pdf
in entire directory tree of the directory containing the batch file working independent on file system and for both directory structures with either the PDF files in directory with date in directory name or in a subdirectory dayreports
. It avoids processing approved*.pdf
more than once by checking if a file to move/rename contains already the folder name (date) and so can be executed multiple times on entire directory tree.
@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "%~dp0approved*.pdf" /A-D-H /B /S 2^>nul') do (
if exist "%%~dpI\un%%~nxI" del /F "%%~dpI\un%%~nxI"
if exist "%%~dpI\%%~nI_V*%%~xI" ( del /F "%%I" ) else call :MoveFile "%%I"
)
endlocal
rem Avoid a fall through to the subroutine.
goto :EOF
rem The subroutine MoveFile assigns path of passed file name ending with
rem a backslash to environment variable FilePath. Next the backslash is
rem removed from file path. Then is checked if the file path ends with
rem folder name "dayreports" in which case also this folder is removed
rem from file path. The file path is processed by command FOR to get
rem the string after last backslash referenced with %%~nxJ which is the
rem name of the folder of which name should be in new file name. Before
rem moving the file with new name containing the date, it is checked if
rem the current file name ends already with an underscore and the name
rem of the folder which should be the date. The subroutine is exited
rem if this condition is true to avoid double processing an already
rem processed file in a previous execution of this batch file.
:MoveFile
set "FilePath=%~dp1"
set "FilePath=%FilePath:~0,-1%"
if /I "%FilePath:~-11%" == "\dayreports" set "FilePath=%FilePath:~0,-11%"
for %%J in ("%FilePath%") do set "FolderName=%%~nxJ"
set "FileName=%~n1"
if "%FileName:~-11%" == "_%FolderName%" goto :EOF
move /Y %1 "%FilePath%\%~n1_%FolderName%%~x1" >nul
goto :EOF
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 /?
... explains also %~dp0
which expands to drive and path of argument 0 which is the full path of the batch file ending always with a backslash.
del /?
dir /?
echo /?
endlocal /?
for /?
if /?
move /?
rd /?
ren /?
set /?
setlocal /?