cmd for loop mass renaming again oneliner - for-loop

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.

Related

How to get displayed a system variable read from external txt file with FOR /F expanded?

I am making a query on a txt file via Windows command prompt, but the output displayed is actual content of txt file, instead of system variable.
Any idea how can I amend that?
FOR /F "tokens=1,2 delims=," %%G IN (Backup_Folder.txt) DO (
echo %%H
)
Output is
%USERPROFILE%\Documents\Electronic Arts
instead of
C:\Users\UserName\Documents\Electronic Arts
Content of Backup_Folder.txt is:
EA,%USERPROFILE%\Documents\Electronic Arts
I suggest to use in the batch file:
for /F "tokens=1* delims=," %%G IN (Backup_Folder.txt) do call echo %%H
It is better to use tokens=1* instead of tokens=1,2 in case of the folder path stored in the text file contains per chance also a comma, for example:
EA,%USERPROFILE%\Documents,Settings\Electronic Arts
The asterisk after 1 means the rest of the line after the comma(s) after first comma delimited string without further splitting up the line on commas. So with tokens=1* the loop variable G gets assigned EA and loop variable H gets assigned %USERPROFILE%\Documents,Settings\Electronic Arts. The usage of tokens=1,2 results in H gets assigned just %USERPROFILE%\Documents, for that example.
The command CALL is necessary in this specific case to get on execution of the body of the loop after replacing %%H by %USERPROFILE%\Documents\Electronic Arts in first parsing step the command line parsed a second time by Windows command interpreter to expand additionally %USERPROFILE%.
When the folder path read from text file is assigned to an environment variable, I recommend to use as command set "FolderPath=%%~H" or call set "FolderPath=%%~H" for getting all environment variable references in folder path already expanded before assigning the folder path to the environment variable.
The tilde character between %% and H results on a folder path like "%USERPROFILE%\Documents & Settings\Electronic Arts" stored in the text file that the surrounding double quotes are removed making it possible to use the environment variable for example in a command line like:
if exist "%FolderPath%\Game\game.exe" echo Game exists.
A space as well as &()[]{}^=;!'+,`~ in a file/folder name without or with path require the usage of surrounding double quotes as output by Windows command interpreter on last help page on running in a command prompt window cmd /?. In argument strings not being a file/folder name/path the additionally possible characters |<> require also that the entire argument string is enclosed in straight double quotes to get those redirection operators interpreted as literal characters.
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 /?
for /?
set /?

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

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
)

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.

Create with pipe a user defined filename batch file

I want to read from stdin a filename for a batch file that I need to create. All in a single line and using pipe.
When I try the cmd is creating the .bat before I put in the name.
set /p filename= | copy NUL %a%.bat
That's not how the pipe works. The pipe takes the output of the previous command, and makes it the input of the next command. Your set /p filename= command doesn't produce any output, so the copy NUL %a%.bat doesn't get any input, but that's irrelevant anyway, because the copy command doesn't take any input anyway. Your copy NUL %a%.bat command creates an empty file called .bat because you haven't defined a variable called a, and so %a% gets expanded to the empty string.
What you want to do requires two commands:
set /p filename=
copy NUL %filename%.bat
As a line in a batch file, use
SET /p "filename=Filename ? "&CALL COPY NUL %%filename%% >nul
If you are entering this at the prompt, reduce %% to %
Note that the >nul suppresses the file copied message.
To complement Klitos Kyriacou's helpful answer:
A pipe is definitely not the right choice, but & and && can in principle be used to execute multiple commands on a single line, so you may be tempted to do the following:
:: !! WRONG
set /p filename="" && copy NUL "%filename%.bat"
The problem is that variable reference %filename% is expanded before execution of the command line begins.[1]
However, if you turn on delayed expansion (via cmd /V, or, inside a batch file with setlocal enabledelayedexpansion (which also makes all variables local)), you can make this work, assuming you use !...! to reference your variables:
:: OK, assuming delayed expansion is on.
set /p filename="" && copy NUL "!filename!.bat"
[1] Magoo's answer works around that problem with a trick: call effectively delays expansion, but that requires that %filename% not be expanded up front, which is why the %% are doubled to result in literal % chars.: %%filename%% is initially expanded to literal %filename%, which is then interpreted by call as a variable reference - by which time the variable already has its value.
Since % chars. cannot be escaped on the command line, a different approach is needed: %filename^% achieves the same as above.

How can I wrap the current directory in strings in CMD?

I need to write a command which will change the current directory and print the NEW directory wrapped in some tags. I thought cd SOMEPATH & echo wkd%cd%wkd would do it but there is a problem.
Here is some example input and output
C:\Users> cd .. & echo wkd%cd%wkd
wkdC:\Userswkd
As you can see, the OLD directory was printed. Why does this happen?
I also tried using newlines (since I feed the command though an external program) but that gives problems when starting command line software.
I really hope there is a solution for this.
In batch files, lines or blocks of code (code enclosed in parenthesis) are first parsed, then executed and the process repeated on the next line/block. During the parse phase all read operations to obtain a value from a variable are removed from the code, replaced with the value in the variable before starting to execute the code.
In your case, when the line is parsed %cd% is replaced with its value before starting to execute the line and change the folder.
Alternatives:
If you separate the command in two lines you will retrieve the correct value.
cd ..
echo wkd%cd%wkd
You can enable delayed expansion and change the syntax used to retrieve the variable from %var% to !var! telling the parser the read operation should be delayed until the execution time
rem inside a batch file
setlocal enabledelayedexpansion
cd .. & echo wkd!cd!wkd
rem from a command line
cmd /v:on /q /c "cd .. & echo wkd!cd!wkd"
You can escape the percent signs to the parser does not see a variable and force a command reevaluation before the echo to get the correct value. You can do it with a call command. It works, but call is slower than other options
rem inside a batch file
cd .. & call echo wkd%%cd%%wkd
rem from command line
cd .. & call echo wkd^%cd^%wkd
Instead of reading a variable, you can execute a command that retrieve the required information
rem from command line
cd .. & for %A in (.) do echo wkd%~fAwkd
cd .. & for /f %A in ('cd') do echo wkd%Awkd
rem in batch files the percent sign needs to be escaped
cd .. & for %%A in (.) do echo wkd%%~fAwkd
cd .. & for /f %%A in ('cd') do echo wkd%%Awkd
(%~fA is the full name of the element referenced by the for replaceable parameter %A)
There is a difference in how for /f and for commands in previous code work
for /f is starting a cmd instance that will execute the cd command to output the current directory, output that is processed by the code in the do clause that is invoked for each output line, with the line stored in the replaceable parameter
for without modifiers directly retrieves a reference to the element indicated, in this case ., the current folder. In this case %~fA is used to obtain a real full name from the relative . reference into an absolute path
All this options are only doing one thing: delay the retrieval of the current folder until the cd .. has been executed.
Give this a try:
cd .. & For /F %A in ('cd') do echo wkd%Awkd
The for loop thing does command output capture as found here: Put command output into a variable
Then I just echo that variable with the stuff around it.
The reason why your original try didn't work is that the %cd% environment variable is set before any of the command executes and is not changed until the interpreter goes on to the next command entirely. It treats foo & bar as a single command with two parts.

Resources