0

Following on from a previous question: PowerShell/Batch script to rename each file to include original name + folder name

I have the following folder structure, for several months:

2017
 │
 ├─April reports
 │   ├─01-04-2018
 │   │ ├─XYZAPPROVED123.pdf
 │   │ ├─XYZAPPROVED123_V2.pdf
 │   │ └─XYZUNAPPROVED123.pdf
 │   │
 │   ├─02-04-2018
 │   │ ├─XYZAPPROVED123.pdf
 │   │ └─XYZUNAPPROVED123.pdf
 │   │
 │   │
 │   └─30-04-2018
 │     ├─XYZAPPROVED123.pdf
 │     └─XYZUNAPPROVED123.pdf
 │ 
 ├─ May reports
 │   ├─01-04-2018
 │   │ ├─XYZAPPROVED123.pdf
 │   │ ├─XYZAPPROVED123_V2.pdf
 │   │ └─XYZUNAPPROVED123.pdf
 │   │
 │   ├─02-04-2018
 │   │ ├─XYZAPPROVED123.pdf
 │   │ └─XYZUNAPPROVED123.pdf
 │   │
 │   │
 │   ├─30-04-2018
 │      ├─XYZAPPROVED123.pdf
 .
 .
 .
 ├─December reports (etc)

Each folder for each day in the month contains an approved and an unapproved report with the same name ("XYZAPPROVED123" or "XYZUNAPPROVED123"). Some contain a V2.

I want to rename each one so that the "123" is removed and the folder name (the date) is included in the filename e.g. "XYZAPPROVED_01-04-2018". I don't want to completely replace the filename with the name of the folder, I just want it added on at the end separated by an underscore if possible.

Once I've done that, I'd like to delete all the files that contain "XYZUNAPPROVED" in the name and delete any "XYZAPPROVED" files where there is an "XYZAPPROVED_V2" in the same folder (version 2 supersedes the original).

I have managed to get a batch script to work for one month's (April) reports using the script below. The issue I am having is adding an additional loop so that I can run it for all the months.

April script (this works):

@echo off
cd "<actual path>"
for /D %%I in ("<actual path>\????-??-??") do (
    for %%J in ("%%I\XYZ*.pdf") do (
        if exist "%%I\XYZUN*" del /F "%%I\XYZUN*.pdf"
        if exist "%%I\%%~nJ_V*.pdf" ( del "%%J" ) else ren "%%J" 
"%%~nJ_%%~nI.pdf"
    )
)

all months script- (nothing happens when I run it)

@echo off
cd "<actual path>"
 for /D %%H in ("<actual path>") do (
 rem Enter into the month name folder
  rem process each date
  for /D %%I in ("%%H\????-??-??") do (

      for %%J in ("%%I\XYZ*.pdf") do (
      if exist "%%I\XYZUN*" del /F "%%I\XYZUN*.pdf"
      if exist "%%I\%%~nJ_V*.pdf" ( del "%%J" ) else ren "%%J" "%%~nJ_%%~nI.pdf"

  )

 )
)

What am I missing?

ellemgcf
  • 77
  • 1
  • 2
  • 7

2 Answers2

2

Here is a solution that accomplishes what you want -- see all the explanatory comments (rem):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_ROOT=%~dp0TEST"       & rem // (path of the root directory)
set "_DIR1=????"            & rem // (mask for level-1 sub-directories (year))
set "_DIR2=* reports"       & rem // (mask for level-2 sub-directories (month))
set "_DIR3=??-??-????"      & rem // (mask for level-3 sub-directories (day))
set "_NAMEREN=approved"     & rem // (partial name of files to keep)
set "_NAMEDEL=unapproved"   & rem // (partial name of files to delete)
set "_MASKREN=*%_NAMEREN%*" & rem // (name pattern for files to keep)
set "_MASKDEL=*%_NAMEDEL%*" & rem // (name pattern for files to delete)
set "_FILEEXT=.pdf"         & rem // (name extension for all files to process)
set "_VERSUFF=V"            & rem // (beginning of version suffix in file names)
set "_KEEPSUFF=#"           & rem // (set non-empty to keep version suffix)

rem // Build search string for version suffix:
set "SEARCH=_%_VERSUFF%[0123456789][0123456789]*"
rem // Walk through all level-1 sub-directories:
for /D %%C in ("%_ROOT%\%_DIR1%") do (
    rem // Walk through all level-2 sub-directories:
    for /D %%D in ("%%~C\%_DIR2%") do (
        rem // Walk through all level-3 sub-directories:
        for /D %%E in ("%%~D\%_DIR3%") do (
            rem // Loop through all files to delete:
            for %%F in ("%%~E\%_MASKDEL%%_FILEEXT%") do (
                rem // Delete current file:
                del "%%~F"
            )
            rem // Store path and name of current sub-directory:
            set "LOC=%%~E" & set "SUB=%%~nxE"
            rem // Loop through cached and sorted list of all files to potentially keep:
            for /F "delims= eol=|" %%F in ('
                2^> nul dir /B /A:-D /O:N "%%~E\%_MASKREN%%_FILEEXT%"
            ') do (
                rem // Store file name and extension:
                set "VER=" & set "FILE=%%~nF" & set "EXT=%%~xF"
                rem // Check if current file name has got a version suffix:
                echo(%%~nF| > nul findstr /I /E "%SEARCH%" && (
                    rem // Version suffix encountered, hence extract it:
                    setlocal EnableDelayedExpansion
                    for %%H in ("!FILE:_=" "!") do (
                        endlocal
                        set "VER=_%%~H"
                        setlocal EnableDelayedExpansion
                    )
                    endlocal
                    rem /* Check whether there are other files with same name but with another
                    rem    version suffix appended: */
                    for %%I in ("%%~E\%_MASKREN%_%_VERSUFF%*%_FILEEXT%") do (
                        rem // Filter for correct version suffix:
                        echo(%%~nI| > nul findstr /I /E "%SEARCH%" && (
                            rem // Extract version suffix:
                            set "NUM=" & set "TEST=%%~nI"
                            setlocal EnableDelayedExpansion
                            for %%J in ("!TEST:_=" "!") do (
                                endlocal
                                set "NUM=_%%~J"
                                setlocal EnableDelayedExpansion
                            )
                            rem /* Check whether the other version suffix is higher (or equal)
                            rem    than the one of the current file:
                            if !VER:*_%_VERSUFF%^=! lss !NUM:*_%_VERSUFF%^=! (
                                rem // Other version suffix is higher, so delete current file:
                                if exist "!LOC!\!FILE!!EXT!" del "!LOC!\!FILE!!EXT!"
                            )
                            endlocal
                        )
                    )
                ) || (
                    rem /* No version suffix encountered, hence check if there is a file with
                    rem    the same name but with a version suffix appended: */
                    for %%G in ("%%~E\%%~nF_%_VERSUFF%*%%~xF") do (
                        rem // Filter for correct version suffix:
                        echo(%%~nG| > nul findstr /I /E "%SEARCH%" && (
                            rem // Delete current file as there is a newer version:
                            if exist "%%~E\%%~F" del "%%~E\%%~F"
                        )
                    )
                )
                rem // Check whether current file has been deleted:
                if exist "%%~E\%%~F" (
                    rem // Keep version suffix only if desired:
                    if not defined _KEEPSUFF set "VER="
                    rem // Extract the partial file name to keep:
                    setlocal EnableDelayedExpansion
                    for /F "delims=| eol=|" %%K in ("_!FILE:%_NAMEREN%=|!") do (
                        endlocal
                        set "NAME=%%K%_NAMEREN%"
                        setlocal EnableDelayedExpansion
                    )
                    rem // Rename kept file as desired:
                    ren "!LOC!\!FILE!!EXT!" "!NAME:~1!_!SUB!!VER!!EXT!"
                    endlocal
                )
            )
        )
    )
)

endlocal
exit /B

This script deletes the following files from each day-related sub-directory:

  • all *.pdf files that contain unapproved in their names;
  • the *.pdf file with approved in its name but without a version suffix _V, in case there is another *.pdf file with approved in its name but with a version suffix appended;
  • the *.pdf files with approved in their names and a version suffix _V, whose appended version numbers are not the greatest one encountered;

The script keeps and renames the following files in each day-related sub-directory:

  • the *.pdf file with approved in its name and a version suffix _V, whose appended version number is the greatest one encountered; the part before approved is kept, the part after that is replaced by _ plus the parent directory name; the version suffix can be kept optionally (see constant _KEEPSUFF);
aschipfl
  • 33,626
  • 12
  • 54
  • 99
1

Here is the last batch file from this answer rewritten for new requirement of a fixed prefix at beginning of each file name defined at top of the batch file.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Prefix=XYZ"
for /F "delims=" %%I in ('dir "%~dp0%Prefix%Approved*.pdf" /A-D-H /B /S 2^>nul') do call :ProcessFile "%%I"
endlocal
rem Avoid a fall through to the subroutine.
goto :EOF

rem The subroutine ProcessFile first assigns full qualified file name
rem of found approved PDF file to environment variable FullFileName.
rem All occurrences of defined prefix after a backslash are substituted
rem by backslash, defined prefix and additionally the two characters UN.
rem A PDF file with that name in same directory as the found approved PDF
rem is deleted if existing which means there is still an unapproved PDF
rem file for an already approved PDF file.

rem Next the subroutine checks if there is one more approved PDF file
rem starting with same name, but there is additionally _V and any other
rem string in which case the approved PDF file without _V* in name is
rem deleted and subroutine is exited.

rem Otherwise the subroutine 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.

:ProcessFile
setlocal EnableDelayedExpansion
set "FullFileName=%~1"
set "UnapprovedFile=!FullFileName:\%Prefix%=\%Prefix%Un!"
if exist "%UnapprovedFile%" del /F "%UnapprovedFile%"
endlocal

if exist "%~dp1\%~n1_V*%~x1" (
    del /F %1
    goto :EOF
)

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

There is a very easy method to delete all *unapproved*.pdf files in all directories of the directory tree independent on existence of a *approved*.pdf file in same directory:

del /A /F /Q /S "%~dp0*unapproved*.pdf"

The command DEL with the used parameters deletes

  • quiet because of /Q
  • all *unapproved*.pdf in directory of batch file and all its subdirectories because of /S
  • including *unapproved*.pdf with read-only attribute set because of /F
  • including *unapproved*.pdf with hidden attribute set because of /A.

The setlocal ... endlocal block in subroutine ProcessFile can be removed on using this command line first at top of the batch file before the FOR loop which results in a much faster execution of the batch file.

I don't know if it is advisable to delete all *unapproved*.pdf without checking if there is a corresponding *approved*.pdf in same directory.

Mofi
  • 46,139
  • 17
  • 80
  • 143