-1

I'm having problems with string literals in renaming files:

I have a number of files that contain the substring x and I want to substitute them for y which may occur in any part of the filename.

cheese.txt
Sarah_cheese_Parker.txt
cheese_Brookes.txt

should become:

Louise.txt
Sarah_Louise_Parker.txt
Louise_Brookes.txt

set x="cheese"
set y="Louise"

This is what I have so far

for %%f in (x*.txt) do 
(
    echo %%f
    call :processit "%%f"
)
goto finished

:processit
ren x%i%.txt y%i%.txt

goto :finished

:finished
dir *.txt /b

I'm not worried about complications with % in the filename.

Ghoul Fool
  • 6,249
  • 10
  • 67
  • 125
  • The left parenthesis needs to be on the same line as the DO. Use the `SET` command to do string replacement before the rename. – Squashman Jan 05 '18 at 20:45

2 Answers2

1

The batch file should be stored in directory containing all the *.txt files and executed with a double click.

@echo off
for /F "eol=| delims=" %%I in ('dir "*cheese*.txt" /A-D /B 2^>nul') do call :RenameFile "%%I"
set "NewName="
pause
goto :EOF

:RenameFile
set "NewName=%~nx1"
set "NewName=%NewName:cheese=Louise%"
ren "%~1" "%NewName%"
goto :EOF

The command DIR searches

  • just for files because of /A-D (not attribute directory)
  • only in current directory as not using additionally /S
  • for *.txt files containing cheese and
  • outputs in bare format because of /B the names of the found files with file extension, but without path.

The DIR command line is executed by FOR in a separate command process in background which captures the output of this command process (= command DIR) written to handle STDOUT.

A possible error output by DIR on not finding in directory any file matching the wildcard pattern to handle STDERR is suppressed by redirecting it with 2>nul to device NUL. 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.

The command FOR processes each line captured from output of DIR. The default behavior of FOR to ignore lines starting with ; is disabled by option eol=|. The character | can't be used in a file/folder name and so can't be at begin of a file/folder name. The default behavior of FOR to split up each line on horizontal tabs and spaces into substrings is disabled by option delims= specifying an empty list of delimiters. So FOR really assigns each file name with file extension, but without path to loop variable I.

It is important here to run FOR on a captured list of file names instead of letting FOR itself search recursively for the TXT files as otherwise it could happen during the file renames that some TXT files are skipped because of the directory entry changes during the loop iterations.

For each file name the subroutine RenameFile is called with passing the full file name as first argument enclosed in double quotes to the subroutine. The double quotes are necessary to process correct also the file names on containing a space or one of these characters &()[]{}^=;!'+,`~ in file name or file path.

A subroutine is used instead of doing the remaining code directly in the FOR loop as this would require usage of delayed environment variable expansion which would cause problems if file/folder names of the processed files contain by chance one or more exclamation marks.

In the subroutine RenameFile the name of the current file without path is assigned to environment variable NewName.

Next a string substitution is applied on file name to replace case-insensitive all occurrences of cheese by Louise.

Then the file rename is executed with specifying the file to rename and the new name with file extension without path as required by command REN.

The command ECHO could be inserted left to ren to run the batch file first for just checking if the file renames would be done as expected.

The DIR option /S could be added after /B to search additionally also recursive in all subdirectories of current directory for *.txt files with cheese in file name. In this case DIR would output all file names with full path. But the batch file as written would work nevertheless correct.

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 /?
  • dir /?
  • echo /?
  • for /?
  • goto /?
  • pause /?
  • ren /?
  • set /?

See also Where does GOTO :EOF return to?

Mofi
  • 46,139
  • 17
  • 80
  • 143
1

This code do what you want. Note that *%x%*.txt is the right wild-card for "files that contain the substring x which may occur in any part of the filename".

@echo off
setlocal EnableDelayedExpansion

set "x=cheese"
set "y=Louise"

for %%f in (*%x%*.txt) do (
    echo %%f
    call :processit "%%f"
)
goto finished

:processit
set "name=%~1"
ren "%~1" "!name:%x%=%y%!"
exit /B

:finished
dir *.txt /b

The keypoint in this solution is that the replacement of one substring by another one can only be done in a variable, so it is necessary to assign the %1 subroutine parameter to the name variable.

The second point is that in order to replace one substring by another one, when both strings are stored in variables, you need to use a standard %expansion% to first expand the x and y substrings, and then a delayed !expansion! to perform the replacement in name variable; this is achieved in this line:

ren "%~1" "!name:%x%=%y%!"

A final point is that you don't really need a subroutine in this case, so the final code could be simpler:

@echo off
setlocal EnableDelayedExpansion

set "x=cheese"
set "y=Louise"

for %%f in (*%x%*.txt) do (
    echo %%f
    set "name=%%f"
    ren "%%f" "!name:%x%=%y%!"
)
dir *.txt /b
Aacini
  • 65,180
  • 12
  • 72
  • 108