Search code examples
windowsbatch-filereplacerenamebulk-rename-utility

Search directory tree and replace instances of a string with another string


EDIT:

New Code thanks to the help of @Magoo

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION


echo      NOTE: This will create all necessary project template folders. 
echo[

set /p projNumName="     Enter Project Number & Name (ex. 23-000_My Project Name): "

SET projectName=%projNumName%
SET sourcedir="Y:\2023 Projects\%projectName%"

MkDir "Y:\2023 Projects\%projNumName%"

xcopy "W:\_PROJECT TEMPLATES-do not delete_\ProjTemplate\"  "Y:\2023 Projects\%projectName%\" /e


PUSHD "%sourcedir%"
FOR /f "delims=" %%e IN ('dir /s /b /a:-d "Y:\2023 Projects\%projectName%\*template*" ' ) DO (
 SET "filename=%%~nxe"
 SET "filename=!filename:template=%projectname%!"
 REN "%%e" "!filename!"
)

FOR /f "delims=" %%e IN ('dir /s /b /ad "Y:\2023 Projects\%projectName%\*template*" ^|sort /rev' ) DO (
 SET "dirname=%%~nxe"
 SET "dirname=!dirname:template=%projectname%!"
 REN "%%e" "!dirname!"
 )
)


popd

dir/s /b /on "Y:\2023 Projects\%projectName%"

echo[
echo[
echo[
echo      Template folders and files have been copied to your project folder.
echo[
echo[
echo[
echo[

PAUSE

GOTO :EOF

end of edit

I found this post, but I don't have enough rep points to comment on it. It's effectively nearly identical to my situation. I can't use PowerShell, and I need to recursively search for a string ("Template") and replace it with another, which the user inputs.

Here are the important parts of the code I have so far:

@echo off

echo THIS WILL CREATE YOUR PROJECT FOLDERS
echo[

SET /p projectName="Enter Project Number and Name (ex. 23-000_My New Project): "

MkDir "Y:\%projectName%"
MkDir "T:\Final Project Files 2023\%projectName%"

xcopy "W:\_PROJECT TEMPLATES_\Template\" "Y:\%projectName%\" /e

echo[
echo Template folders and files have been copied to the new project folder.
echo[
echo Rename any instances of "Template" to match your new project name.
echo[

PAUSE

START "" "C:\Program Files\Bulk Rename Utility\Bulk Rename Utility.exe"

The code I have works for the situation, but I'm attempting to eliminate the last bit where Bulk Rename Utility launches and requires the user to manually bulk rename the folders and files that contain "Template".

What I get now is:

Y:\23-000_My New Project\Template-Folder1
Y:\23-000_My New Project\Template-Folder2
Y:\23-000_My New Project\Template-Folder2\Template.txt

I would like to have:

Y:\23-000_My New Project\23-000_My New Project-Folder1
Y:\23-000_My New Project\23-000_My New Project-Folder2
Y:\23-000_My New Project\23-000_My New Project-Folder2\23-000_My New Project.txt

Solution

  • Very important:

    It's rare for a batch file to not start

    @echo off
    setlocal
    

    The first command instructs cmd to NOT echo commands
    The second establishes a local environment with a copy of the parent environment.

    The local environment is exited when the batch terminates, restoring the parent environment. Consequently, any variables altered when the rest of the code runs are undone.

    As it stands, your code would set projectName into the environment for that instance of cmd until it is explicitly removed.

    setlocal can also take some arguments that alter cmd's behaviour within the local environment - see setlocal /? from the prompt for documentation.

    By default, EXTENSIONS is enabled and DELAYEDEXPANSION is disabled.

    Since this code uses delayedexpansion (see Stephan's DELAYEDEXPANSION link) you need to use

    @ECHO OFF
    SETLOCAL ENABLEDELAYEDEXPANSION
    

    as your first two lines.

    *-------- the meat of the matter

    Just after the xcopy add:

    FOR /f "delims=" %%e IN ('dir /s /b /a-d "Y:\%projectName%\*" ' ) DO (
     SET "filename=%%~nxe"
     IF /i "!filename:~0,8!" == "template" (
      SET "filename=%projectname%!filename:~8!"
      ECHO REN "%%e" "!filename!"
     )
    )
    FOR /f "delims=" %%e IN ('dir /s /b /ad "Y:\%projectName%\*" ' ) DO (
     SET "dirname=%%~nxe"
     IF /i "!dirname:~0,8!" == "template" (
      SET "dirname=%projectname%!dirname:~8!"
      ECHO REN "%%e" "!dirname!"
     )
    )
    

    Always verify against a test directory before applying to real data.

    This scans the newly-created directory subtree for files and if the first 8 characters are template (in either case) then the file is renamed to the projectname concatenated with the filename(except for the first 8 characters) - see set /? from the prompt for docco.

    That process is then repeated for the directory names.

    Note:
    The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the files/directories.

    --- revision -----

    @ECHO OFF
    SETLOCAL ENABLEDELAYEDEXPANSION
    
    SET "sourcedir=u:\your files"
    SET "projectName=23-000_My New Project"
    
    PUSHD "%sourcedir%"
    FOR /f "delims=" %%e IN ('dir /s /b /a-d "%projectName%\*template*" ' ) DO (
     SET "filename=%%~nxe"
     SET "filename=!filename:template=%projectname%!"
     ECHO REN "%%e" "!filename!"
    )
    
    FOR /f "delims=" %%e IN ('dir /s /b /ad "%projectName%\*template*" ^|sort /rev' ) DO (
     SET "dirname=%%~nxe"
     SET "dirname=!dirname:template=%projectname%!"
     ECHO REN "%%e" "!dirname!"
     )
    )
    
    popd
    
    dir/s /b /on "u:\your files"
    
    GOTO :EOF
    

    You would need to change the value assigned to sourcedir to suit your circumstances. The listing uses a setting that suits my system. I deliberately include spaces in names to ensure that the spaces are processed correctly.

    This version uses dir to find the instances of template. The : is documented as being optional - for me, it worked without.

    The issue with the directory name not being correctly processed should be fixed by the ^|sort /rev. The ^ is required to tell cmd that the pipe is part of the command to be executed, not of the for.

    By sorting the directorynames in reverse, every child directory found appears before its parent, so is renamed first.