Why does ECHO command print some extra trailing space into the file? - windows

I am reading multiple lines from a data file and append it to another file. The issue is that some extra space is in output file at end of appended lines. It is mandatory to trim the trailing space according to my requirement because in original data file there is no space at end of the lines. I tried all possible things, but I still cannot achieve this one.
#echo off
setlocal enabledelayedexpansion
FOR /F "delims=" %%c IN (C:\Users\184863\Desktop\mydata.txt) DO (
echo %%c>>temp_data.txt
)
endlocal
Options I tried:
echo %%c>>temp_data.txt ... getting 1 space end of line
set lines=%%c
echo !lines!>>temp_data.txt ... getting 1 space end of line
echo !lines:~0,-1!>>temp_data.txt ... last char of the line trimmed
set lines=!lines: =!
echo !lines!>>temp_data.txt ... all spaces inside the line are also trimmed (need to trim only end of the line, which is not present in original data file)

There is a trailing space at end of the echo line as Magoo wrote already which is not ignored by ECHO. This trailing space is also output by ECHO after %%c.
A redirection is usually written at end of a command line, but it can be written anywhere. It is also possible to write the redirection at beginning as shown below or somewhere in the middle as it can be seen on FOR command line in this answer. Parsing of an ECHO command line is different to all other command lines as a space character outside a double quoted string is not interpreted as argument separator and for that reason each space character in ECHO command line matters and is output by ECHO including those before redirection operators like >.
This can be seen easily by
creating a batch file with just echo Test>Test.txt  with a trailing space after Test.txt,
opening a command prompt window, and
running this batch file from within command prompt window.
Output is echo Test  1>Test.txt. So > is replaced by Windows command interpreter by  1> (space, one, right angle bracket) and additionally moved to end of line. This movement on ECHO command line results in originally trailing space being moved left after the text Test and is therefore also output by ECHO.
This command line modification during preprocessing of the entire command block can be seen also by running original posted batch code without #echo off from within a command prompt window to debug the batch file. Windows command interpreter outputs with mydata.txt containing the single line [code=119888#!198; ttps://egrul.nalog.ru/]:
setlocal enabledelayedexpansion
FOR /F "delims=" %c IN (C:\Users\184863\Desktop\mydata.txt) DO (echo %c 1>>temp_data.txt )
(echo [code=119888#!198; ttps://egrul.nalog.ru/] 1>>temp_data.txt )
endlocal
The trailing space after >>temp_data.txt is now after preprocessing the entire FOR command line with the command block containing only a single command left to  1>>temp_data.txt. When FOR executes the ECHO command line, the trailing space being now an additional space between line read from mydata.txt and  1>>temp_data.txt is also output by ECHO.
It should be always taken into account what is really executed by Windows command interpreter after preprocessing/parsing a command line and not what is written in the batch file.
Furthermore delayed environment variable expansion is enabled which results in line read from the file referenced with %%c being processed by Windows command interpreter for !VariableName! before executing the ECHO command. Everything between two exclamation marks in line is interpreted as variable name and therefore replaced by value of the referenced variable respectively nothing if there is no such environment variable. A single (remaining) exclamation mark is simply removed by Windows command interpreter during this additional processing of the line before ECHO execution.
Solution 1:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "usebackq delims=" %%I in ("%USERPROFILE%\Desktop\mydata.txt") do >>temp_data.txt echo(%%I
endlocal
Delayed environment variable expansion is explicitly disabled in this batch code to interpret exclamation marks always as literal characters.
There is used ( instead of a space character between the command echo and the loop variable reference %%I to output a line starting with /? correct instead of the usage help of command ECHO as it would happen on using echo %%I and a line read from the text file mydata.txt starts with /? after 0 or more leading spaces/tabs.
The text file with the line(s) to read is specified using predefined Windows environment variable USERPROFILE enclosed in double quotes to work on any Windows computer. The double quotes require the option usebackq to get the file name string with path interpreted as text file name from which to read lines.
As a line perhaps ends with a number in range 0 to 9 it is not good to use:
echo %%c>>temp_data.txt
It is better to specify the redirection operator at beginning of the ECHO command line and specify next the echo %%c with no trailing space. Please read the Microsoft documentation about Using command redirection operator for more information.
Please note that FOR ignores empty lines and also all lines starting with a semicolon with the used options. ; is the default for option eol (end of line) which is not explicitly specified in this batch file.
On running this small batch file with #echo off modified to #echo ON from within a command prompt window instead of double clicking on it, it can be seen what Windows command interpreter really executes:
for /F "usebackq delims=" %I in ("C:\Users\184863\Desktop\mydata.txt") do echo %I 1>>temp_data.txt
echo [code=119888#!198; ttps://egrul.nalog.ru/] 1>>temp_data.txt
So it can be seen which command line is finally executed by Windows command interpreter after preprocessing each command line of the batch file before execution. >> was replaced by  1>> (note inserted space at beginning) and the redirection at beginning was moved to end of ECHO command line.
Another solution is enclosing the command ECHO in round brackets to form a command block and redirect the output of this command block with just command ECHO to a file, i.e. use (echo %%c) >>temp_data.txt. The number of spaces after ) do not matter anymore. Therefore (echo %%c)>> temp_data.txt with a space after redirection operator >> and a trailing space at line end results in same text in file temp_data.txt as on using >>temp_data.txt echo %%c.
Solution 2:
#echo off
copy /B temp_data.txt+"%USERPROFILE%\Desktop\mydata.txt" temp_data.txt >nul
The COPY command can be use to merge contents of multiple files specified with + operator together into a single file specified last. This feature is used here to append to existing temp_data.txt the lines from mydata.txt on the user´s desktop. The option /B (binary data) is needed to avoid that COPY appends to output file temp_data.txt ^Z which is the substitute control character SUB with hexadecimal code value 1A.
This solution is definitely better than the first one as it does not matter what the file mydata.txt contains.
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.
copy /?
echo /?
endlocal /?
for /?
setlocal /?

You have a terminal space on the line echo %%c>>temp_data.txt (amongst others) which should be deleted. This is typical of the confusion that terminal spaces causes.

This should do the needful, including the need to comment line 100.
One: Don't use delayed expansion when it isn't necessary.
Two: Remove the trailing space in your echo command.
#(
SETLOCAL
ECHO OFF
SET "_File=C:\Users\184863\Desktop\mydata.txt"
SET "_Count="
SET "_Comment=Your Special Comment "
)
FOR /F "tokens=* delims=" %%c IN (%_File%) DO (
SET /A "Count+=1">NUL
CALL ECHO.[%COUNT%] | FIND "[100]">NUL && (
ECHO:%_Comment%%~c
) || (
ECHO.%%c
)
)>>"%~dp0temp_data.txt"
REM Replace the original file with the commented File
MOVE /Y "%~dp0temp_data.txt" "%_File%"
(
ENDLOCAL
EXIT /B 0
)

Related

How to read some lines from a file and output them with changed and added characters into another file?

I wrote a batch script for my development environment variables, but have some troubles because I'm not familiar with Windows batch scripts.
This is what I tried so far:
echo window._env_ = { > ./env-config.js
findstr /v # .env >> ./env-config.js
echo } >> ./env-config.js
It outputs this:
window._env_ = {
REACT_APP_SITE_TITLE=MyWebsite
REACT_APP_SITE_URL=http://localhost:3000
BROWSER=none
REACT_APP_API_ENDPOINT=http://localhost:5000
}
These are the environment variables stored in the file .env which I want to write into the file env-config.js:
#
# Website Title
REACT_APP_SITE_TITLE=MyWebsite
#
# Website URL
REACT_APP_SITE_URL=https://my-website-url.org
#
# Define if the Browser auto-open at react start
BROWSER=none
#
# Define the API Endpoint for the Backend
REACT_APP_API_ENDPOINT=http://localhost:5000
The final env-config.js should look like this:
window._env_ = {
REACT_APP_SITE_TITLE: "MyWebsite",
REACT_APP_SITE_URL: "https://my-website-url.org",
BROWSER: "none",
REACT_APP_API_ENDPOINT: "http://localhost:5000",
}
So what I need to do is to set the variable values in the output in quotes, change = to : and add a comma at the end. But I can´t find a solution combined with findstr.
Does anyone have an idea how to get the wanted output from the input?
FINDSTR outputs all lines containing a searched string or a string matched by a regular expression respectively with the usage of option /V all lines NOT containing a searched string or a string matched by a regular expression (inverted result). Therefore FINDSTR can be used to filter out the lines starting with #, but the other lines are output as stored in the file .env which is of no help here as the lines not starting with # should be written reformatted into the file env-config.js.
The solution is using a FOR /F loop to process each line in the text file .env and output the lines not starting with # with command ECHO with the values of interest reformatted as required redirected into the file env-config.js.
The batch file solution with just a single command line:
#(echo window._env_ = {& (for /F "eol=# tokens=1* delims==" %%I in (.env) do #echo %%I: "%%J",) & echo })> .\env-config.js
# at beginning of a command line executed just once (everything after first #) or multiple times (echo %%I: "%%J", inside the loop) during the batch file execution prevents the output of the command line after parsing and before executing it. Batch files contain usually at top #echo off to turn off command echo mode for all further command lines in the batch file and prevent also the output of this first command line.
The opening round bracket ( after first # defines a command block which ends with the last closing parenthesis ) left to redirection operator >. The Windows Command Processor cmd.exe processing the batch file opens first the file env-config.js in the current directory as defined with the relative path .\ for write operations and truncates the file to zero length if that file exists already because of everything output to handle STDOUT (standard output) during execution of the command block should be redirected into this file. The file is kept open until the execution of all commands inside the command block finished.
The ampersand & outside a double quoted argument string is interpreted as unconditional AND command operator. For more details about unconditional and conditional (&& and ||) command operators see single line with multiple commands using Windows batch file.
The command block on a single command line contains here three commands which are executed once.
ECHO is different to most other Windows commands as it does not interpret a space character as argument string separator, but as character to output. For that reason there is no space between { and & to avoid that this space is output by ECHO resulting in a trailing space in the file env-config.js.
Which commands are executed can be seen easier by splitting the single command line version up into multiple command lines.
#echo off
(
echo window._env_ = {
for /F "eol=# tokens=1* delims==" %%I in (.env) do echo %%I: "%%J",
echo }
)> .\env-config.js
The inner command block defined by ( left to for and ending with ) after , is not needed anymore in this case because of the line termination defines where the FOR command line executing multiple times the command ECHO ends. In the single command line version the two round brackets are needed to let cmd know where the command line to execute by FOR on each loop iteration ends.
There is first written into the opened file env-config.js the string window._env_ = { output by command ECHO with carriage return and line-feed as line termination (18 characters/bytes).
Next the file .env in current directory is opened for read operations because of option /F of command FOR and the file name .env neither enclosed in " (processing of string) nor in ' (processing of captured output of a command line).
The default behavior of FOR /F on processing the lines of a text file is
ignoring empty lines which are lines not containing any character than the line ending characters carriage return and line-feed,
splitting the line up into substrings using normal space and horizontal tab as string delimiters with removal of all leading spaces/tabs from the line,
looking on first character of first substring on being a semicolon which is the default end of line character in which case the line is not further processed,
assigning the first space/tab delimited substring not starting with ; to the specified loop variable and
executing the command respectively the commands in the command block specified after do.
This line splitting behavior is modified with the options eol=# tokens=1* delims== specified within " to be interpreted as one argument string after option /F to
split up each non-empty line on equal signs as defined with delims==,
ignore all lines starting with the character # as defined with eol=#,
whereby two substrings are wanted, the first substring and everything after the equal sign(s) after first substring kept as is without splitting this part of the line up further on equal signs as defined with tokens=1* and
assign the first equal sign delimited substring to specified loop variable I and if there is something more on the line to the next loop variable J according to ASCII table.
So the lines starting with # in file .env are ignored and on other lines the string left to first (series of) = is assigned to the loop variable I (name) and everything after first (series of) = is assigned to the loop variable J (value) which can be also an empty string if there is nothing after first equal sign.
The two strings are output with command ECHO with a colon and a space between name and value and with enclosing the value in double quotes and with a comma at end of the line and this output is written with carriage return and line-feed into the opened file env-config.js.
Last after all lines of .env are processed by FOR /F and this file is closed, there is output with ECHO the string } with the newline characters carriage return and line-feed.
The execution of all commands of the command block is finished with echo } and so cmd closes the file env-config.js containing now all the output characters/bytes.
The current directory is not necessarily the directory containing the batch file. So if it is expected that the file .env is in the directory of the batch file and the file env-config.js should be created also in the directory of the batch file, it would be better to use the following single command line batch file.
#(echo window._env_ = {& (for /F "usebackq eol=# tokens=1* delims==" %%I in ("%~dp0.env") do #echo %%I: "%%J",) & echo })> "%~dp0env-config.js"
The multi-line version is:
#echo off
(
echo window._env_ = {
for /F "usebackq eol=# tokens=1* delims==" %%I in ("%~dp0.env") do echo %%I: "%%J",
echo }
)> "%~dp0env-config.js"
%~dp0 (drive and path of batch file argument 0) expands to the full directory path of the batch file always ending with a backslash and therefore concatenated without one more \ with the file names .env and env-config.js.
The FOR /F option usebackq is used additionally to get the fully qualified file name of the file .env in the batch file directory enclosed in " interpreted as file name of a text file of which lines to process and not as string to process.
Please note that the directory separator on Windows is \ and not / as on Linux/Mac as described by Microsoft in the documentation about Naming Files, Paths, and Namespaces. The usage of / instead of \ in file/folder strings usually work thanks to the automatic correction applied by the Windows I/O functions as explained by the referenced Microsoft documentation page. But there are use cases on which the usage of / in a file/folder string results in unexpected behavior as described with two examples in the answer on How can I check if either or file exists in IF Exist?
For completeness a multi-line batch file which outputs the lines in file .env in directory of the batch file into the file env-config.js in directory of the batch file without a comma on the last line read from .env.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FirstLine=1"
(
echo window._env_ = {
for /F "usebackq eol=# tokens=1* delims==" %%I in ("%~dp0.env") do (
if not defined FirstLine (echo ,) else set "FirstLine="
set /P =%%I: "%%J"<nul
)
echo(
echo }
)> "%~dp0env-config.js"
endlocal
There can be removed both %~dp0 to use the current directory instead of the batch file directory.
The IF condition results in the output of a string with a comma, a carriage return and a line-feed if the currently processed line is not the first line with data to output read from the file .env.
The command SET with option /P is used here with no environment variable name to output the reformatted data to handle STDOUT without a carriage return and line-feed which does not wait for user input because of the device NUL is used as STDIN (standard input) which cannot be opened to read something.
The additional command line echo( results in the output of just carriage return and line-feed to terminate also the last line with data read from .env not having a comma at end.
These are special "hacks" not officially documented by Microsoft, but documented in many answers on Stack Overflow and in DosTips forum topics ECHO. FAILS to give text or blank line - Instead use ECHO/
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 /? ... explains batch file argument referencing as done with %~dp0
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
See also the Microsoft documentation about Using command redirection operators.

Why are special characters missing in array of lines output by Windows batch file?

I want to insert data (only rows having extended keyword) present in .txt file into Oracle database in the format ID,Data,Date,Project Name where ID, date and project name are present in environment variables.
File.txt has below data:
Writing main object(name=abc)
writing Extended object (name=%abc(123&rest,type=pqr)
logdata.txt should have below data:
A1234C,(name=%abc(123&rest,type=pqr),12022018_11:12:20,DEV:Sales Project
While copying the data, special characters like %,( etc present in the file.txt are missing in the output file logdata.txt.
Please find below code :
set file=D:\MSTR_CICD\file.txt
for /F "usebackq tokens=2*delims=(" %%a in (`findstr "extended" "%file%"`) do (
set /A i+=1
call set array[%%i%%]=%%a
call set n=%%i%%
)
for /L %%i in (1,1,%n%) do call echo %User_ID%,%%array[%%i]%%,%Timestamp%,%proj% >> D:\MSTR_CICD\Batch_Script\logdata.txt
Please correct the code or let me know how can i achieve this. Also, my input file can have any special character as it contain logs of an application.
This batch file can be used for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "proj=DEV:Sales Project"
set "User_ID=A1234C"
set "Timestamp=12022018_11:12:20"
set "InputFile=D:\MSTR_CICD\file.txt"
set "DataFile=D:\MSTR_CICD\Batch_Script\logdata.txt"
if exist "%InputFile%" (
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /I /C:Extended "%InputFile%"') do (
set "DataLine=%%I"
setlocal EnableDelayedExpansion
set "DataLine=!DataLine:*(=(!"
set "DataLine=!DataLine:"=""!"
echo %User_ID%,"!DataLine!",%Timestamp%,%proj%
endlocal
)
) >"%DataFile%"
if exist "%DataFile%" for %%I in ("%DataFile%") do if %%~zI == 0 del "%DataFile%"
:EndBatch
endlocal
FINDSTR runs in the separate command process started by FOR in background with cmd.exe /C a case-insensitive, literal search for the string Extended on the input file and outputs all lines containing this string to handle STDOUT.
FOR captures this output and processes them line by line. FOR ignores empty lines and by default also lines starting with a semicolon because of ; is the default end of line character. And FOR splits up the line into substrings (tokens) using space/tab as delimiter and assigns just the first substring to specified loop variable by default.
By using the FOR option string delims^=^ eol^= an empty list of delimiters and and no end of line character is set to disable line splitting and ignoring lines starting with a semicolon. As this special option string cannot be enclosed in double quotes, it is necessary to escape the space and the two equal signs with caret character to get those three characters outside a double quoted argument string interpreted as literal characters and not as argument string separators.
The entire line as output by FINDSTR found in file is assigned to environment variable DataLine. This is done with delayed environment variable expansion disabled to process also lines correct containing one or more exclamation marks. Otherwise cmd.exe would double parse the line set "DataLine=%%I" after having replaced %%I by the current line and would interpret every ! in the line as begin/end of an environment variable reference resulting in unwanted modification of the line before assigning it to the environment variable.
The usage of command CALL on a line with command SET results also in double parsing the command line before executing the command SET which is the reason why some characters are missing in the environment variables array produced by your code.
For details see also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
After having assigned the line to the environment variable, it is necessary to enable delayed expansion to further process the data line in the FOR loop. That makes the batch file slow, but can't be avoided in this case. Read this answer for details about the commands SETLOCAL and ENDLOCAL.
The first modification on the data line is removing everything left to first (.
The second modification on the data line is replacing all " by "" in the line to escape every double quote according to CSV specification.
Then the remaining data line is output together with the other data enclosed in double quotes as the data line can contain also one or more commas which requires according to CSV specification that the data is enclosed in double quotes.
For CSV specification read for example the Wikipedia article about comma-separated values.
Everything output by ECHO inside FOR loop is redirected to the specified data file which overwrites a by chance already existing data file with same name.
It is possible that FINDSTR does not find any line containing Extended in any case resulting in producing a data file with 0 bytes. The empty data file is deleted by the second FOR.
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 /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?

including special char (&) from "for delims"-result in batch file

I have a script that reads thru file and sets variable as it finds it.
#echo off
Setlocal EnableDelayedExpansion
for /f "tokens=*" %%V in ('findstr /I /C:title= "%~1"') do set title=%%V
echo %title%
In the txt file there is "title=variable & speed".
And the script only returns:
title=variable
'SPEED' is not recognized as an internal or external command,
operable program or batch file.
As it should return whole line.
This is the part, I have not found the solution yet. It should change the "&" to "-", as in finally this script renames files.
First, don't enable delayed expansion because of not needed here. It can result in findstr does not find the file to open if the batch file is called with a file name without or with a path containing one or more exclamation marks.
Second, the FOR option "tokens=*" results in removing leading spaces/tabs from a line output by FINDSTR not starting with a semicolon and if there is something left assign the rest of the line to specified loop variable. This is okay if this behavior is wanted here. Otherwise it would be better to use "delims=" which defines an empty list of delimiters resulting in assigning the entire line not starting with a semicolon to the specified loop variable. Not double quoted argument string delims^=^ eol^= defines an empty list of delimiters and no end of line character to get assigned also a line starting with a semicolon to the loop variable. The two equal signs and the space character must be escaped with caret character ^ to be interpreted as literal characters and not as argument separators.
Third, an ampersand outside a double quoted argument string is interpreted as operator to unconditionally execute the command after & after executing the command before &. For details see Single line with multiple commands using Windows batch file. For that reason it is recommended to enclose the argument string of command SET in double quotes as explained in detail on answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line?
So I suggest using following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LoadedTitle="
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1" 2^>nul') do set "LoadedTitle=%%V"
if defined LoadedTitle (
setlocal EnableDelayedExpansion
echo !LoadedTitle!
endlocal
)
endlocal
Read this answer for details about the commands SETLOCAL and ENDLOCAL.
Please note that /C:"title=" is used instead of /C:title= on FINDSTR command line as otherwise FINDSTR would in this special case search just for title. The reason is that the command line within the round brackets is executed in a separate command process started by FOR with cmd.exe /C in background and the equal sign not enclosed in a double quoted string and not escaped with ^ would be removed by current command process because of being interpreted as separator. In a command prompt window it is possible to use the FINDSTR command line with /C:title= without double quotes, but not here on this FOR command line in batch file.
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 findstr command line with using a separate command process started in background.
Temporary enabling delayed expansion just for output of the line with loaded title string is required because of usage of only echo %LoadedTitle% would be modified before execution to echo title=variable & speed and the ampersand is again not interpreted as literal character to output by ECHO, but as operator to run speed after execution of echo title=variable .
I recommend to read
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
How to debug a batch file?
A batch file writer must always take into account what is finally executed by Windows command processor after parsing a command line one or more times as this can be different than what is written in batch file on using environment variable references with syntax %variable%.
I Found the correct "formula" to script
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=*" %%V in ('%SystemRoot%\System32\findstr.exe /I /C:"title=" "%~1"
2^>nul') do set "title=%%V"
set title=!title:^&=-!
echo "!title!"
endlocal
As it does now that what I wanted, it returns:
"title=variable - SPEED"
It is not as you suggested but it does the job.

Batch script to print last line of many files in a folder matching a file name pattern

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.

cmd for loop mass renaming again oneliner

I'm over my head with this - spent too much time searching already - evidently I don't understand the basics of CMD variables etc. - and it always gives me such a headache
why wouldn't this work?
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
the above code outputs the value of %tmpx% in some other scope - and it is always constant
yes, i run setlocal ENABLEDELAYEDEXPANSION
basically i need to do a simple rename of all files in folder from constantstring_somenameXX.tif to somenameXX.tif, where i.e. constantstring=0000000005
i had to use set because other posts rightly suggested that %a in a for loop has a special behaviour, and the substitutions wouldn't work for it as it is.
i would prefer not to use scripts and/or powershell - unless not using them is impossible
thank you
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
The problem with the previous code is delayed expansion. Yes, you enabled it, but you have not used it, and depending on how you enabled it, it will not work
In cmd, when a line or block of lines (code inside parenthesis) is reached, it is first parsed and then executed. During the parse phase, variable read operations are removed from the command, replaced with the value in the variable before the command starts to execute. So, if you change the value of a variable inside a line/block you can not retrieve the changed value inside the same line/block as there are no variable reads (they were replaced)
setlocal enabledelayedexpansion allows to replace (where needed) the variable read syntax from %var% to !var!, indicating to the parser that the read operation will be delayed until the execution phase.
So, in your case, your code should have been something like
setlocal enabledelayedexpansion & for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )
BUT this will not work (in default configured environments).
cmd has two execution modes: batch file and command line. In your case, you are using command line (no escaped percent sign in for loop) and in command line mode the setlocal enabledelayedexpansion will not work. It is intended for batch files (see setlocal /?)
How to make it work from the command line? By default cmd is started with delayed expansion disabled and you can not enable it if not inside a batch file. But you can start cmd with delayed expansion enabled and run your command in this started instance (see cmd /?)
cmd /v:on /c "for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )"
Anyway, to solve your rename problem, delayed expansion is not needed
for %a in (*_*.tif) do for /f "tokens=1,* delims=_" %b in ("%~nxa") do echo ren "%a" "%c"
That is, for each tif file with an underscore, take the name and extension of the file (%~nxa) as a string, and using the underscore as a delimiter between tokens, retrieve the first token (the text on the left of the first underscore) in %b and the rest of the text (to the right of the underscore) into %c. Now, just rename the original file name (stored in %a) to the contents of %c (the text on the right of the underscore)
In this code rename operations are only echoed to console. If the output is correct, remove the echo command.
! is the character to use rather than % when wanting execution time value. % does when it's read value.
CMD was written by IBM engineers and they were trying to make MSDos a programming language while making sure Dos commands ran the same. So we get a hodge podge.
& seperates commands on a line.
&& executes this command only if previous command's errorlevel is 0.
|| (not used above) executes this command only if previous command's errorlevel is NOT 0
> output to a file
>> append output to a file
< input from a file
| output of one command into the input of another command
^ escapes any of the above, including itself, if needed to be passed to a program
" parameters with spaces must be enclosed in quotes
+ used with copy to concatinate files. E.G. copy file1+file2 newfile
, used with copy to indicate missing parameters. This updates the files modified date. E.G. copy /b file1,,
%variablename% a inbuilt or user set environmental variable
!variablename! a user set environmental variable expanded at execution time, turned with SelLocal EnableDelayedExpansion command
%<number> (%1) the nth command line parameter passed to a batch file. %0 is the batchfile's name.
%* (%*) the entire command line.
%<a letter> or %%<a letter> (%A or %%A) the variable in a for loop. Single % sign at command prompt and double % sign in a batch file.

Resources