Home > Net >  Filename editing with batch
Filename editing with batch

Time:11-30

So I'm working on a script for file renaming/editing and I'm getting an error
The system cannot find the file specified

I'm assuming it's because of the loop, how can I fix this

@echo off
Setlocal enabledelayedexpansion
cd /d "%~dp0"

for /f "delims=*" %%a IN ('dir /b /s /a-d "Folder1\(*)*.txt"') DO (
    Set "File=%%~nxa"
    Ren "%%a" "!File:(1)=(-125)!"
    Ren "%%a" "!File:(2)=(-124)!"
    Ren "%%a" "!File:(3)=(-121)!"
    Ren "%%a" "!File:(4)=(-117)!"
    Ren "%%a" "!File:(5)=(-120)!"
    Ren "%%a" "!File:(6)=(-116)!"
    Ren "%%a" "!File:(7)=(-115)!"
    Ren "%%a" "!File:(8)=(-127)!"
    Ren "%%a" "!File:(9)=(-126)!"
    Ren "%%a" "!File:(10)=(-100)!"
)

Source Folder1
(1) filename.txt
(2) filename.txt
(3) filename.txt

Source Folder1 Results
(-125) filename.txt
(-124) filename.txt
(-121) filename.txt

As you can see the script does work, but I am getting the message
I also tested it with this for /f "Tokens=*" same results

CodePudding user response:

There can be used the following code for this task.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for /F "delims=" %%I in ('dir "(*)*.txt" /A-D-L /B 2^>nul') do (
    set "FileName=%%I"
    setlocal EnableDelayedExpansion
    set "NewName=!FileName:(1)=(-125)!"
    set "NewName=!NewName:(2)=(-124)!"
    set "NewName=!NewName:(3)=(-121)!"
    set "NewName=!NewName:(4)=(-117)!"
    set "NewName=!NewName:(5)=(-120)!"
    set "NewName=!NewName:(6)=(-116)!"
    set "NewName=!NewName:(7)=(-115)!"
    set "NewName=!NewName:(8)=(-127)!"
    set "NewName=!NewName:(9)=(-126)!"
    set "NewName=!NewName:(10)=(-100)!"
    ren "!FileName!" "!NewName!"
    endlocal
)
popd
endlocal

There is first defined the required execution environment with

  • command echo mode turned off and
  • command extensions enabled and
  • delayed variable expansion disabled.

Next the subdirectory Folder1 of the batch file directory is made the current working directory or the batch file processing is exited if that folder does not exist at all.

Then one more Windows command process is started in background with %ComSpec% /c and the command line within ' of command FOR appended as additional arguments. There is executed with Windows installed into C:\Windows:

C:\Windows\System32\cmd.exe /c dir "(*)*.txt" /A-D-L /B 2>nul

The command DIR searches

  • in the current directory
  • for just files because of /A-D-L (attribute not directory and not link (reparse point))
  • with a file name matched by the wildcard pattern (*)*.txt and
  • outputs just the matching names without path in bare format because of /B.

It is possible that DIR does not find a matching file name in which case an error message is output which is suppressed by redirecting it from STDERR (standard error) to device NUL.

Read the Microsoft documentation 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.

FOR with option /F captures all output to STDOUT (standard output) of started cmd.exe and waits for self-termination of started cmd.exe before the captured output is processed line by line.

Empty lines are ignored which do not exist here at all. There would be split up by default the file names into substrings using normal space and horizontal tab as string delimiters, analyzed if the first substring begins with a semicolon in which case the line (file name) would be also ignored while otherwise the first space/tab delimited substring is assigned to the specified loop variable I before running the commands in body of FOR.

The line (file name) splitting behavior is not wanted which is the reason for usage of the option delims= to define an empty list of string delimiters to turn off the line splitting into substrings. The default end of line character ; can be kept in this case as all file names to process start definitely with character (.

The file name without path is first assigned to the environment variable FileName which works also for file names containing one or more exclamation marks as delayed variable expansion is disabled at execution of this command line.

Next is enabled delayed variable expansion as required for the next commands. Please read this answer for details about the commands SETLOCAL and ENDLOCAL and what really happens in memory of running cmd process on using these two commands.

A series of string substitutions is done next with command SET to define the new file name based on the current file name before running just once the command REN to rename the current file to the new name.

The code posted in the question tries to rename the file multiple times. It fails on string substitution does not change anything at all because of a file cannot be renamed on new name being equal the current name. Then one REN command works on which the string substitution was successful. The other REN command fail again because of the file is already renamed and renaming it once again with original file name cannot work anymore for that reason.

The initial environment with disabled delayed variable expansion is restored last before processing the next file name.

There could be used for this task also:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for %%# in ("1 125" "2 124" "3 121" "4 117" "5 120" "6 116" "7 115" "8 127" "9 126" "10 100") do for /F "tokens=1,2" %%G in (%%#) do for %%I in ("(%%G)*.txt") do for /F "tokens=1* delims=)" %%J in ("%%I") do ren "%%I" "(-%%H)%%K"
popd
endlocal

For each pair of current and new number a string splitting is done to assign the current number to the loop variable G and the new number to the loop variable H before a FOR is used to process all files with current number (G) of which file name is assigned to loop variable I which is split up on first occurrence of a closing round bracket to get the part after first ) from entire file name with file extension assigned to the loop variable K to be able to rename the file with the new number.

There are several other solutions possible too.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • call /? ... explains %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
  • dir /?
  • echo /?
  • endlocal /?
  • exit /?
  • for /?
  • popd /?
  • pushd /?
  • ren /?
  • set /?
  • setlocal /?

Read also single line with multiple commands using Windows batch file for an explanation of conditional command operator || used on third command line.


The first batch file solution with processing also files in subfolders:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for /F "delims=" %%I in ('dir "(*)*.txt" /A-D-L /B /S 2^>nul') do (
    set "FullName=%%I"
    set "FileName=%%~nI"
    setlocal EnableDelayedExpansion
    set "NewName=!FileName:(1)=(-125)!"
    set "NewName=!NewName:(2)=(-124)!"
    set "NewName=!NewName:(3)=(-121)!"
    set "NewName=!NewName:(4)=(-117)!"
    set "NewName=!NewName:(5)=(-120)!"
    set "NewName=!NewName:(6)=(-116)!"
    set "NewName=!NewName:(7)=(-115)!"
    set "NewName=!NewName:(8)=(-127)!"
    set "NewName=!NewName:(9)=(-126)!"
    set "NewName=!NewName:(10)=(-100)!"
    ren "!FullName!" "!NewName!%%~xI"
    endlocal
)
popd
endlocal

The second batch file solution with processing also files in subfolders:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for %%# in ("1 125" "2 124" "3 121" "4 117" "5 120" "6 116" "7 115" "8 127" "9 126" "10 100") do for /F "tokens=1,2" %%G in (%%#) do for /R %%I in ("(%%G)*.txt") do for /F "tokens=1* delims=)" %%J in ("%%~nxI") do ren "%%I" "(-%%H)%%K"
popd
endlocal

The text files with hidden attribute set are ignored by the FOR loop searching recursively in the subdirectory Folder1 of the batch file directory for the files with the number in round brackets assigned to the loop variable G.

  • Related