This question already has answers here:
Variables are not behaving as expected
(1 answer)
Example of delayed expansion in batch file
(5 answers)
At which point does `for` or `for /R` enumerate the directory (tree)?
(1 answer)
Closed 1 year ago.
I'm trying to write a batch script that'll read all the pdf files in a folder and rename them such that there are no spaces in them. So I've typed up the below code. Although most of the parts of the code seems to work in isolation, I get an error when running the code together.
for /r %%f in (*.txt) do (
set filename=%%~nxf
set new=%filename: =%
ren "%filename%" %new%
)
The filename is detected correctly by line2. But on line3, I don't get the value I've stored in line2. Interestingly enough, if I were to run the command again in the same prompt, line3 then works (filename variable is read correctly). It must be how the for loop operates in a batch script. If I run the below code exactly 3 times in the same command prompt, the code works perfectly fine (I assume because all variables are now set correctly). Can someone please help point me in the right direction? Thanks in advance.
Note: I have a filename called "filename .txt" in the working directory, which I realise wasn't the best choice of filename. :|
(error in screenshot)
Open a command prompt, run set /? and read the output help carefully and completely from top of first to bottom of last page, especially the section about delayed expansion explained also by Variables are not behaving as expected. The Windows command processor cmd.exe processes the entire command block starting with ( and ending with matching ) before executing command FOR at all. All environment variables using %...% syntax are expanded (replaced) already during this processing phase by the appropriate variable expansion result.
So executed is the following on environment variable filename not already defined:
for /R %f in (*.txt) do (
set filename=%~nxf
set new= =
ren ""
)
That can be seen on debugging the batch file by running it from within a command prompt window instead of double clicking the batch file. This results in the following error message for each *.txt file found in current directory and all its subdirectories:
The syntax of the command is incorrect.
The syntax of the command ren is of course incorrect.
One solution is using following batch code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "* *.txt" /A-D /B /S 2^>nul') do (
set "FullName=%%I"
set "FileName=%%~nxI"
setlocal EnableDelayedExpansion
ren "!FullName!" "!FileName: =!"
endlocal
)
endlocal
The first two lines define completely the required execution environment for the batch file.
There is not used a for /R loop as that can cause troubles depending on file system of current drive and the file names to modify on renaming the files with file extension .txt while the FOR loop iterates of the file system entries matching the wildcard pattern.
The usage of the for /F loop results in first starting one more command process in background with %ComSpec% /c and the specified command line appended as additional arguments. So with Windows installed in C:\Windows is executed in background:
C:\Windows\System32\cmd.exe /C dir "* *.txt" /A-D /B /S 2>nul
The second command process runs DIR which
searches in current directory and all its subdirectories because of option /S
for just files because of option /A-D (attribute not directory)
of which name matches the wildcard pattern * *.txt in long or short name
and outputs the found file names in bare format because of option /B which means just the file names with full path because of option /S.
The command DIR finds also matching file names of files with hidden attribute set which are ignored by for /R. The option /A-D could be modified to /A-D-H to ignore hidden files.
The wildcard pattern contains a space character. For that reason the command DIR outputs just the full qualified file names of files which contain at least one space character in long file name. Short 8.3 file names cannot contain a space character.
The error message output by DIR if it cannot find at least one file name matching the wildcard pattern in the entire directory tree of current directory is suppressed by redirecting the error message from handle STDERR 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.
The command FOR respectively the cmd.exe instance processing the batch file captures all lines output by DIR to handle STDOUT of command process started in background. The processing of the list of full qualified file names starts when started cmd.exe closed itself after finishing execution of command DIR.
The list of file names to process is now completely in memory of the command process executing the batch file. The file renames done next by the loop cause multiply changes in file system, but that does not affect the list of file names processed by FOR as it is the case on using for /R. So there is surely no file name with a space in name skipped as the file system changes do not affect the processing of the files to rename.
FOR with option /F results by default in ignoring all empty lines. The command DIR does not output empty lines.
Next a non-empty line is split up by default into substrings using horizontal tab and normal space as string delimiters. That string splitting behavior is definitely not wanted here as the files to rename contain at least one space character. For that reason delims= is used to define an empty list of string delimiters which turns off the line splitting behavior completely.
There is by default ignored next a line of which first substring starts with default end of line character ;. But the command DIR with option /S outputs all file names with full path and it is therefore impossible that any full qualified file name starts with a semicolon. So it is not necessary to modify the default end of line character.
The full file name is assigned to loop variable I which is next assigned to the environment variable FullName. The file name with file extension without path is assigned to environment variable FileName. The environment variables are (re)defined while delayed environment variable expansion is disabled to process also file names correct containing one or more ! in name. If delayed expansion would be already enabled, each exclamation mark in file name assigned to loop variable I would be interpreted as beginning/end of a delayed expanded environment variable reference which of course is not wanted in this case.
Now delayed expansion is enabled to be able to rename the file using its full file name referenced delayed expanded and its new name without path with all spaces removed. Then the previous environment is restored which is necessary to avoid a stack overflow as there is much more done in background by setlocal EnableDelayedExpansion than toggling the state of delayed expansion and to process the next file name again in an environment with delayed expansion disabled. See this answer for details on what happens in background on each usage of the commands SETLOCAL and ENDLOCAL.
There is no guarantee that each file rename really works. The file rename fails if there is already a file or directory with new name in the directory of a file to rename. A file rename fails also if a file to rename is currently opened by an application which opened it with denying any access by another application.
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.
dir /?
echo /?
endlocal /?
for /?
ren /?
set /?
setlocal /?
Related
Hello I am trying to rename all files ending with "VA.pdf" to "PA.pdf" using batch code
I tired this code but it is not working
REN *VA.pdf *PA.pdf
Appreciate any help
There can be used for this file renaming task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if exist "*!*VA.pdf" goto ExtendedVersion
setlocal EnableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir *VA.pdf /A-D-L /B 2^>nul') do (
set "FileNamePDF=%%~nI"
set "FileNameNew=!FileNamePDF:~0,-2!PA%%~xI"
if not exist "!FileNameNew!" ren "!FileNamePDF!%%~xI" "!FileNameNew!"
)
endlocal
goto EndBatch
:ExtendedVersion
echo INFO: Extended version required because of a PDF file with exclamation marks.
for /F "eol=| delims=" %%I in ('dir *VA.pdf /A-D-L /B 2^>nul') do (
set "FileNamePDF=%%~nI"
setlocal EnableDelayedExpansion
set "FileNameNew=!FileNamePDF:~0,-2!PA%%~xI"
if not exist "!FileNameNew!" ren "!FileNamePDF!%%~xI" "!FileNameNew!"
endlocal
)
:EndBatch
endlocal
There is defined first the required execution environment with the first two command lines.
The IF condition in the third command line quickly checks if there is any PDF file with case-insensitive VA in the file name before the file extension .pdf containing one or more exclamation marks in the file name. The extended version of the processing loop is required if this condition is true.
The standard version enables first required delayed expansion. Then a FOR loop is used which runs in background with Windows installed into C:\Windows:
C:\Windows\System32\cmd.exe /c dir *VA.pdf /A-D-L /B 2>nul
The internal command DIR of cmd.exe searches
in the current directory as defined by the process starting cmd.exe for processing the batch file
for just file names because of option /A-D-L (attribute not directory and not link)
matching case-insensitive the wildcard pattern *VA.pdf in long or short 8.3 name
and outputs in bare format because of option /B just the file names with file extension, but without file path.
An error message output to handle STDERR (standard error) on DIR does not find any file system entry matching the criteria is suppressed by redirecting this error message to the 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 respectively cmd.exe processing the batch file captures all output written to standard output stream of in background started cmd.exe and processes it line by line after started cmd.exe closed itself after finishing executing the command DIR.
FOR with option /F is used here to get a list of file names of *VA.pdf files loaded into memory of cmd.exe before really doing the file renames as otherwise it could happen especially on FAT drives (FAT32, exFAT) that some PDF files are skipped or processed more than once (on rename not possible).
FOR on using option /F ignores always empty lines which is no problem here as DIR with the used options does not output empty lines.
FOR would next split up the lines into substrings using horizontal tab and normal space as string delimiters, would look next if first tab/space separated string begins with a semicolon in which case it would also ignore the entire line for further processing, and would otherwise assign just the first tab/space separated string to the specified loop variable I before running the commands in body of FOR.
The default line splitting behavior is not wanted as PDF file names can contain one or more spaces. The usage of the option delims= defines an empty list of delimiters which turns off the line splitting behavior.
It is very unusual but nevertheless possible that a PDF file name begins with ; (semicolon). Such a file name should not be ignored by FOR. The option eol=| defines a vertical bar as end of line character which no file name can contain ever. Microsoft lists the characters not allowed in a file name on Windows file systems in the documentation about Naming Files, Paths, and Namespaces.
The current file name without file extension .pdf is assigned first to the environment variable FileNamePDF.
Next a string substitution is used to get from the string value of the environment variable FileNamePDF the file name without the last two characters VA concatenated with the string PA and the file extension .pdf assigned to the environment variable FileNameNew.
If there is not already a PDF file ending with PA in the file name before the file extension, there is next executed the command REN to rename the *VA.pdf file to *PA.pdf.
The command ENDLOCAL after the loop restores the previous environment before enabling delayed expansion and the command GOTO instructs the Windows Command Processor to continue processing the batch file with the command line below the label EndBatch which contains one more ENDLOCAL to restore the environment on starting the batch file processing.
The extended version is nearly the same as the standard version. The difference is that delayed variable expansion is not enabled on assigning the file name of the current VA.pdf file without the file extension to the environment variable FileNamePDF. That avoids interpreting the exclamation mark(s) in the file name as beginning/end of a delayed expanded variable reference resulting in a manipulation of the file name string before assigning it to the environment variable as it would happen with delayed expansion already enabled.
The extended version enables next delayed variable expansion inside the loop, does the same as the standard version and restores finally the previous environment before processing the next *VA.pdf file.
The extended version is slower because of the environment variables list copy and the other operations made in background by every execution of SETLOCAL as explained in full details in this answer. The command ENDLOCAL in the loop is required to avoid a stack overflow on processing lots of PDF files.
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.
echo /?
endlocal /?
for /?
goto /?
if /?
ren /?
set /?
setlocal /?
I currently have a batch file that loops through a text file which has a list of file names and moves these files to folders based on the date in the file name.
I'm trying now to change this to do something similar but for folders. Get folder names and then move each folder to another location based on the date in the folder name.
Example of folder structure:
Before move:
k:\PLPR1
k:\PLPR1\20210910\data
k:\PLPR1\20210909\data
k:\PLPR1\20210830\data
After move:
l:\PLPR1\2021\FTP Data\September\10 September\data
l:\PLPR1\2021\FTP Data\September\09 September\data
l:\PLPR1\2021\FTP Data\September\30 August\data
The folder moving task can be done with following batch file:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "SourcePath=K:\PLPR1"
set "TargetPath=L:\PLPR1"
set "Month01=January"
set "Month02=February"
set "Month03=March"
set "Month04=April"
set "Month05=May"
set "Month06=June"
set "Month07=July"
set "Month08=August"
set "Month09=September"
set "Month10=October"
set "Month11=November"
set "Month12=December"
for /F "delims=" %%I in ('dir "%SourcePath%" /AD-L /B 2^>nul') do (
set "FolderName=%%I"
set "Year=!FolderName:~0,4!"
set "Month=!FolderName:~4,2!"
set "Day=!FolderName:~6,2!"
for %%I in (Month!Month!) do set "Month=!%%I!"
%SystemRoot%\System32\robocopy.exe "%SourcePath%\%%I" "%TargetPath%\!Year!\FTP Data\!Month!\!Day! !Month!" /E /MOVE /NDL /NFL /NJH /NJS /R:1 /W:5 >nul
)
endlocal
The first two command lines define the required execution environment which is:
command echo mode turned off
command extensions enabled
delayed environment variable expansion enabled
The enabled delayed expansion means that this batch file cannot be used without modification on source or target path containing one or more exclamation marks.
It is further expected by this batch file code that the source folder contains only subfolders with names being the date in format yyyyMMdd.
The usage of for /F with a command line enclosed in ' results in running in background one more cmd.exe with option /c and the specified command line appended as additional arguments. So executed is in background with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c dir "K:\PLPR1" /AD-L /B 2>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.
The command DIR searches
in specified directory for file system entries which
have the directory attribute set and are not reparse points (directory junctions) because of option /AD-L and
outputs in bare format just the names of the folders because of option /B without path.
The error message output by DIR on not finding any directory is suppressed by redirecting it to device NUL.
The output of DIR to handle STDOUT of background command process is captured by FOR respectively cmd.exe processing the batch file and processed line by line after started cmd.exe closed itself after finishing execution of DIR.
FOR with option /F results in always ignoring empty lines which do not occur here at all. A non-empty line would be split up by default into substrings using normal space and horizontal tab as string delimiters. That line splitting behavior would be no problem here as the folder names in format yyyyMMdd do not contain a space character, but there is nevertheless used delims= to define an empty list of string delimiters which makes the processing of the file names a very little bit faster as line splitting behavior is turned off in this case.
If the first substring being now the entire directory name would start with a semicolon, FOR would also ignore the captured line for further processing. This end of line behavior does not matter here as the directory names do not start with a semicolon.
So there is in memory of the command process processing the batch file now a list of directory names which are assigned one after the other to the specified loop variable I and it does not matter anymore what happens next in the source directory during the loop iterations. This is important, especially if the source directory is on a FAT drive (FAT32, exFAT).
The directory name without path is assigned to an environment variable and substitutions are used next with delayed expansion to split the date in name up to year, month and day in month.
There are defined twelve environment variables which hold for each month with two digit month value at end of the variable name the appropriate name of the month. A simple FOR loop is used to concatenate the fixed string Month with the month value determined from directory name do redefine the environment variable Month using delayed expansion by the month name assigned to the dynamically built environment variable name starting with Month and ending with current month value.
Now all required data are available to move everything inside the current directory with ROBOCOPY to the appropriate destination directory using the year, month and day values. ROBOCOPY creates automatically the entire destination (target) directory tree.
It is of course possible that ROBOCOPY fails to delete a file/directory in source directory, for example if a file is currently opened in source directory by another application or a directory is the current directory of a running process. But that does not matter for the FOR loop as the command FOR itself does not access anymore the file system to get next directory name because of processing a list of directory names in memory of the command process. So it is not possible that any directory is processed more than once, or that a directory is skipped by chance, or the FOR loop runs into an endless loop thanks to first getting the list of directories to process loaded into memory.
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.
dir /?
echo /?
endlocal /?
for /?
robocopy /?
set /?
setlocal /?
I'm fairly new to batch, my problem is the following:
I have a long list of folders and need to delete the first 3 characters from each of their names. Think 01_Folder1, 02_Folder2, 03_Folder3 and so on. I've tried patching together pieces of CMD commands I've found on the web but could not come up with a script that does what I want it to do. I've even tried using VBScript as I'm more familiar with VB in general but failed to find a solution as well.
Is there an easy way to solve this?
Edit:
Here's my attempt; it's giving me a syntax error but as I am not versed enough in CMD, I cannot really see why:
setlocal enableextensions enabledelayedexpansion
for /d %%i in ("%~dp0*") do (set name=%%i && ren "!name!" "!name:~3!")
endlocal
The FOR command line does not work because of assigned to loop variable i is the name of a directory with full path and so removing the first three characters results in removing drive letter, colon and backslash from path of the directory and not the first three characters from directory name. Further the full qualified directory name is assigned with an additional space to environment variable name because of the space between %%i and operator &&.
One solution would be:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /d %%i in ("%~dp0*") do set "name=%%~nxi" && ren "%%i" "!name:~3!"
endlocal
The disadvantage of this solution is that directory names with one or more exclamation marks in name or path are not processed correct because of enabled delayed expansion resulting in interpreting ! in full directory name as begin/end of a delayed expanded environment variable reference.
Another solution is quite simple with using just the command line:
#for /D %%i in ("%~dp0*_*") do for /F "tokens=1* delims=_" %%j in ("%%~nxi") do #ren "%%i" "%%k"
The outer FOR searches in directory of the batch file for non-hidden subdirectories matching the pattern *_*.
For each directory name assigned with full path to loop variable i one more FOR command is used which processes just the string after last backlash (directory name without path) and splits the string up into substrings (tokens).
The string delimiter is an underscore as defined with option delims=_. The option tokens=1* tells FOR to assign first underscore delimited string to specified loop variable j and everything after one or more underscores after first underscore delimited string to next but one loop variable k according to ASCII table.
The inner FOR would ignore a directory name on which first substring starts with a semicolon as being the default end of line character. But in this case no directory has ; at beginning of its name.
There is one problem remaining with this command line. It does not work on drives with FAT32 or exFAT as file system, just by chance on drives with NTFS file system. The reason is that the list of non-hidden directories changes in file system while the outer FOR iterates over the directory entries matching the pattern.
A better solution loads first the list of directories to rename into memory of Windows command process which is processing the batch file before starting with renaming the directories.
#for /F "delims=" %%i in ('dir "%~dp0*_*" /AD-H /B 2^>nul') do for /F "tokens=1* delims=_" %%j in ("%%i") do #ren "%~dp0%%i" "%%k"
FOR executes in this case in background one more command process with %ComSpec% /c and the command line within ' appended as additional arguments. So executed in background is with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c dir "C:\Batch\File\Path\*_*" /AD-H /B 2>nul
DIR searches in directory of the batch file for
non-hidden directories because of option /AD-H (attribute directory and not hidden)
matching the wildcard pattern *_*
and outputs just the directory names in bare format because of option /B without path to handle STDOUT (standard output) of background command process.
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 respectively the Windows command process processing the batch file captures everything written to standard output handle of background command process and starts processing it as described above after started cmd.exe terminated itself. So there is in memory already a list of directory names and so the executed REN command does not result anymore in a changed list of directory names on processing one after the other.
Please note that a directory with name 01__Underscore_at_beginning is renamed to Underscore_at_beginning and not to _Underscore_at_beginning by both single line solutions.
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.
echo /?
endlocal /?
for /?
ren /?
set /?
setlocal /?
I am trying to iterate files in a folder and process them with another batch file inside the do loop. It works with echo but as soon as I use the variable as input to the program, it echoes the () part and everything inside.
Here's what I'm trying to do.
#echo off
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
for /r %%f in (/folder/*) do (
set name="%%~nf"
echo !name! <--- ok
process.bat !name! <--- echoes () and commands inside this do loop
)
ENDLOCAL
The process.bat just capitalizes the first letter of the filename and echoes it for debug or confirmation.
A batch file must be called from within a batch file using command call as otherwise Windows command processor continues processing on other batch file with never returning back to initial batch file.
See also: How to call a batch file that is one level up from the current directory?
Please read excellent answer on batch file echo on/off not working properly written by dbenham for the reason on getting suddenly the commands executed by FOR output after first execution of process.bat without using command CALL. I cannot explain better what happens in this case.
The directory separator on Windows is the backslash character \ and not the forward slash / as on Linux or Mac. Windows supports also / in file/folder paths for compatibility reasons by automatically replacing all / by \ before accessing the Windows file systems, but a good written script uses 100% correct syntax and does not depend on automatic corrections done by other programs. / is used on Windows mainly for command line switches.
The usage of / instead of \ can result in an unexpected behavior. For example run a batch file with following content:
#echo off
echo Files in directory %SystemRoot:\=/%/:
for %%I in (%SystemRoot:\=/%/*) echo %%I
echo/
pause
echo/
echo Files in directory %SystemRoot%\:
for %%I in (%SystemRoot%\*) echo %%I
echo/
pause
The first FOR using C:/Windows/* as wildcard pattern outputs the file names with just drive letter + colon + file name + file extension. The file path \Windows\ is missing in output file names. The second FOR loop using C:\Windows\* as wildcard pattern outputs the full qualified file names, i.e. drive letter + colon + file path + file name + file extension.
A file/folder path starting with \ references a directory or file relative to root directory of current DRIVE.
This is explained by the Microsoft documentation Naming Files, Paths, and Namespaces.
It looks like folder is a subdirectory in directory of the executed batch file. In this case / or \ at beginning of folder path is definitely not correct. The backslash at beginning can be omitted or .\ is used to reference the directory folder in current directory on execution of the batch file. But the current directory on batch file execution can be also different to directory containing the executed batch file, for example on running the batch file as administrator, or on running the batch file as scheduled task, or on running the batch file from a network resource accessed using a UNC path. For that reason it is advisable to reference explicitly subdirectory folder in directory of the batch file.
Delayed environment variable expansion is not needed as long as the file name assigned currently to the loop variable does not need to be modified other than the modifiers of for support it. A command line like set name="%%~nf" does not work correct with enabled delayed expansion and file name contains one or more ! because of cmd.exe interprets the exclamation mark(s) in file name as beginning/end of a delayed expanded environment variable reference.
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
It looks like a recursive search for non-hidden files is not really needed as otherwise passing just file name without path and file extension would be not enough to get the right file processed by other batch file process.bat.
So the entire task can be done most likely also with:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for %%I in ("%~dp0folder\*") do call "%~dp0process.bat" "%%~nI"
endlocal
But if the other batch file process.bat expects that the passed file name without file extension and path is in current directory on execution of process.bat, it is necessary to make the subdirectory folder in directory of this batch file first the current directory.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0folder"
for %%I in (*) do call "%~dp0process.bat" "%%~nI"
popd
endlocal
Note: The batch file folder path referenced with %~dp0 always ends with a backslash. Therefore no additional backslash should be used on concatenating this path string with a file/folder name to avoid having finally on execution of the batch file \\ in full qualified file/folder name, although Windows kernel corrects such paths also automatically by removing second backslash in this case.
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 /?
echo /?
endlocal /?
popd /?
pushd /?
set /?
setlocal /?
I have multiple text files in a folder. I would like to print the last line of each file found in the folder matching the file name pattern *.Config and redirect those lines to a new text file.
The below script works for 1 text file. But at the moment I do a for loop for multiple text files and %lastline% prints always the same value.
#echo off & setlocal EnableDelayedExpansion
for /f %%i in ('dir /b *.Config') do (
for /r "delims==" %%a in (%%i) do (
set lastline=%%a
echo %lastline% is the last line of %%i >> vResult.txt
)
)
Use this batch code to get written into file vResult.txt in current directory the last line of each *.config file in current directory:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
del vResult.txt 2>nul
for %%I in (*.config) do (
for /F "usebackq delims=" %%L in ("%%I") do set "LastLine=%%L"
echo !LastLine! is the last line of %%I>>vResult.txt
)
endlocal
First command extensions as needed for the second FOR command line and delayed environment variable expansion as needed for echoing the last line of each file are enabled with second command line. Command extensions are enabled by default, but not delayed expansion.
Then a perhaps already existing vResult.txt is deleted with suppressing the error message output by command DEL to handle STDERR by redirecting it to device NUL in case of the file to delete does not exist.
The outer FOR searches for *.config files in current directory with hidden attribute not set. There is no need to use command DIR here except it would be necessary to find also hidden *.config files.
The inner FOR processes the lines of each *.config file found by the outer FOR loop. It is in general advisable to enclose the file name in double quotes and use usebackq to interpret the double quoted file name as file name and not as string to process in case of a *.config file contains a space character. delims= disables splitting each line up on spaces/tabs to get entire line.
As the environment variable LastLine is defined/modified within a command block, it is necessary to use delayed expansion as enabled at beginning with referencing the environment variable with exclamation marks instead of percent signs.
All environment variables referenced with percent signs of entire command block to execute on each iteration of outer FOR are replaced already by Windows command interpreter with current value of referenced environment variable before executing outer FOR command. This resulted on your batch code with ECHO command line additionally in wrong loop in replacing %lastline% by nothing before the outer FOR is executed the first time.
For debugging a batch file and to see what Windows command interpreter really executes after preprocessing the command lines and the command blocks, remove or comment out #echo off or change that line to #echo on, open a command prompt window, and run the batch file from within this console window by entering its name with full path enclosed in double quotes after changing the current directory to the directory on which the batch file should work.
A space character left of redirection operator >> is also output by command ECHO and therefore also written to the text file as trailing space which is the reason why the space character left of >> is removed here. The space character right of >> would be ignored, but is here also removed.
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.
del /?
echo /?
endlocal /?
for /?
setlocal /?
set /?
And see also the Microsoft article Using command redirection operators.