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 /?
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 want to create a batch file able to apply some processing on each JPG file in a folder hierarchy. The following script file works very well for that case (here I only echo the name of each file, but this should be replaced by some more complex statements in the real application):
:VERSION 1
#echo off
set "basefolder=C:\Base"
for /r %basefolder% %%f in (*.jpg) do echo %%f
Actually, I don't want to explore all the folder hierarchy under %basefolder%, but only a given list of subfolders. This modified script is able to deal with that case :
:VERSION 2
#echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
pushd %basefolder%\%%~s"
for /r %%f in (*.jpg) do echo %%f
popd
)
Is there a solution to remove the pushd/popd pair of statements, to get something closer to the initial script. I thought that one of the following scripts would do the job:
:VERSION 3
#echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
for /r %basefolder%\%%~s" %%f in (*.jpg) do echo %%f
)
or, using delayed expansion:
:VERSION 4
#echo off
setlocal enabledelayedexpansion
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
set "folder=%basefolder%\%%~s"
echo !folder!
for /r !folder! %%f in (*.jpg) do echo %%f
)
but none of them is working. When running the second one, the echo !folder! command in the external loop shows C:\Base\A, C:\Base\B and C:\Base\C as expected, but the inner loop doesn't echo any JPG file, so I guess that the recursive for /r command does not run correctly.
What am I doing wrong ?
Final edit after answers :
Thanks to #aschipfl who provided a link to the answer posted by #jeb on another question, quoted below:
The options of FOR, IF and REM are only parsed up to the special character phase. Or better the commands are detected in the special character phase and a different parser is activated then. Therefore it's neither possible to use delayed expansion nor FOR meta-variables in these options.
In other words, my versions 3 and 4 do not work because when defining the root folder of the FOR /R command, neither the %%~s nor the !folder! are correctly expanded by the expression parser. There is no way to change that, as this is a parser limitation. As I said in a comment below: the root folder option in the FOR /R command is basically only syntactic sugar to avoid the use of pushd/popd before and after the command. As this syntactic sugar is incomplete, we have to stick to the original syntax for some specific use cases, as the one presented here. The alternatives proposed by #Gerhard (using a subroutine CALL) or by #Mofi (parsing the result of a DIR command) are working, but they are neither more readable nor more efficient than the simple pushd/popd version I proposed initially.
My Approach for this would be really straight forward:
#echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do for /R "%basedir%" %%a in ("%%~i\*.jpg") do echo %%~fa
The double quotes inside of the subfolders variable is important here, it will ensure that folder names with whitespace are not seen as separators for the folder names. For instance:
set "subfolders="Folder A","Folder B","Folder C""
Edit
#echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do call :work "%%~i"
goto :eof
:work
for /R "%basedir%\%~1" %%a in (*.jpg) do echo %%~fa
It is in general not advisable to assign the value of a loop variable to an environment variable and next use the environment variable unmodified without or with concatenation with other strings being coded in batch file or defined already above the FOR loop within body of a FOR loop. That causes just problems as it requires the usage of delayed expansion which results in files and folders with one or more ! are not correct processed anymore inside body of the FOR loop caused by double parsing of the command line before execution, or command call is used on some command lines, or a subroutine is used called with call which makes the processing of the batch file much slower.
I recommend to use this batch file for the task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "basefolder=C:\Base"
set "subfolders=A B C "Subfolder D" SubfolderE"
for %%I in (%subfolders%) do for /F "delims=" %%J in ('dir "%basefolder%\%%~I\*.jpg" /A-D /B /S 2^>nul') do echo %%J
endlocal
The inner FOR loop starts for each subfolder defined in subfolders in background one more command process with %ComSpec% /c and the DIR command line appended as additional arguments. So executed is with Windows installed to C:\Windows for example for the first subfolder:
C:\Windows\System32\cmd.exe /c dir "C:\Base\A\*.jpg" /A-D /B /S 2>nul
The command DIR searches
in specified directory C:\Base\A and all it subdirectories because of option /S
for files because of option /A-D (attribute not directory) including those with hidden attribute set
matching the pattern *.jpg in long or short file name
and outputs to handle STDOUT of background command process just the matching file names because of option /B (bare format)
with full path because of option /S.
The error message output by DIR on nothing found matching these criteria is redirecting from handle STDERR to device NUL to suppress it.
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 output to handle STDOUT of background command process is captured by FOR respectively the command process which is processing the batch file. FOR processes the captured output line by line after started cmd.exe terminated itself. This is very often very important. The list of files to process is already in memory of command process before processing the first file name. This is not the case on using for /R as this results in accessing file system, getting first file name of a non-hidden file matching the wildcard pattern, run all commands in body of FOR and accessing the file system once again to get next file name. The for /R approach is problematic if the commands in body of FOR change a file to process like deleting, moving, modifying, copying it in same folder, or renaming a found file because of the entries in file system changes while for /R is iterating over these entries. That can easily result in some files are skipped or some files are processed more than once and it could result also an endless running loop, especially on FAT file system like FAT32 or exFAT. It is never good to iterate over a list of files on which the list changes on each iteration.
Command FOR on usage of /F ignores empty lines which do not occur here. A non-empty line is split up into substrings using a normal space and a horizontal tab as string delimiters by default. This line splitting behavior is not wanted here as there could be full qualified file names containing anywhere inside full name one or more spaces. For that reason delims= is used to define an empty list of delimiters which disables the line splitting behavior.
FOR with option /F would also ignore lines on which first substring starts with ; which is the default end of line character. This is no problem here because of command DIR was used with option /S and so each file name is output with full path which makes it impossible that any file name starts with ;. So the default eol=; can be kept.
FOR with option /F assigns by default just first substring to specified loop variable as tokens=1 is the default. This default can be kept here as splitting the lines (full file names) into substrings is disabled already with delims= and so there is always the full file name assigned to the loop variable.
This example uses just echo %%I to output the file names with full path. But it is now safe to replace this single command by a command block which does more with the JPEG files because of the list of JPEG files for each specified subfolder tree in base folder is always already completely in memory of command process processing the batch file.
This question already has an answer here:
At which point does `for` or `for /R` enumerate the directory (tree)?
(1 answer)
Closed 3 years ago.
I can add a prefix to a series of text files using:
:: rename files
for %%a in (*.txt) do (
ren "%%a" "Seekret file %%a"
:: ECHO %%a Seekret file %%a
)
which will turn
a.txt
b.txt
c.txt
into
Seekret file a.txt
Seekret file b.txt
Seekret file c.txt
However, the above code seems to rename the first file twice with the prefix. I end up with
Seekret file Seekret file a.txt
and I have no idea why. Any ideas?
Use
for /f "delims=" %%a in ('dir /b /a-d *.txt') do (
What is happening is that the version you are using sees the renamed-file as a new file. The dir version builds a list of the filenames and then executes the for on each line, so the list is already built and static and cmd isn't trying to operate on a moving target.
Also - use rem, not :: within a code-block (parenthesised sequence of instructions) as this form of comment is in fact a broken label and labels are not allowed in a code block.
Yes, this can happen, especially on FAT32 and exFAT drives because of these file systems do not return the list of directory entries matched by a wildcard pattern to calling executable in an alphabetic order. for processes the directory entries matching *.txt one after the other and the command ren results in changing the directory entries, i.e. the file names list is modified while iterating over it.
The solution is using:
for /F "eol=| delims=" %%I in ('dir *.txt /A-D /B 2^>nul') do ren "%%I" "Seekret file %%I"
FOR runs in this case in background %ComSpec% /c with the command line specified between ' which means with Windows installed into directory C:\Windows:
C:\Windows\System32\cmd.exe /C dir *.txt /A-D /B 2>nul
So one more command process is started in background which executes DIR which
searches in current directory
just for files because of option /A-D (attribute not directory)
including files with hidden attribute set (use /A-D-H to exclude hidden files)
matching the wildcard pattern *.txt
and outputs in bare format just the file names because of option /B.
An error message output by DIR to handle STDERR in case of not finding any directory entry matching these criteria is suppressed by redirecting it to device NUL.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
The file names without path are output by DIR to handle STDOUT of background command process. This output is captured by FOR respectively the command process executing the batch file.
After started command process terminated itself, FOR processes the captured list of file names. All changes done on directory during the loop iterations do not matter anymore for that reason. The file names list does not change anymore.
The options eol=| delims= are needed to get the complete file names assigned one after the other to loop variable I even on starting with ; or containing a space character. eol=| redefines default end of line character ; to a vertical bar which no file name can contain. delims= defines an empty list of delimiters to disable default line splitting behavior on normal spaces and horizontal tabs.
Note: :: is an invalid label and not a comment. Labels inside a command block are not allowed and usually result in undefined behavior on execution of the command block. Use command REM (remark) for a comment.
Even better would be:
for /F "eol=| delims=" %%I in ('dir *.txt /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /I /L /V /C:"Seekret file "') do ren "%%I" "Seekret file %%I"
FINDSTR is used here to output from list of file names output by DIR and redirected to STDIN of FINDSTR all file names which
do not because of /V (inverted result)
begin because of option /B
case-insensitive because of option /I
with the literally interpreted because of option /L (redundant to /C:)
string Seekret file .
Option /C: is needed to specify the search string containing two spaces as using just "Seekret file" would result in searching literally and case-insensitive for either Seekret OR file at begin of a line. In a search string specified with just "..." each space is interpreted by FINDSTR as an OR expression like | in a Perl regular expression string.
A search string specified with /C: is interpreted implicitly as literal string, but with using /R (instead of /L) it would be possible to get this string interpreted as regular expression string on which a space is interpreted as space and not as OR expression. It is possible to specify multiple search strings using multiple times /C:.
My recommendation on using FINDSTR: Use always either /L or /R to make it clear for FINDSTR and for every reader of the command line how FINDSTR should interpret the search string(s) specified with "..." or with /C:"...".
I guess I'll throw my hat in too, since I'm not really a fan of looping through dir output and no one else is currently accounting for this script already having been run:
#echo off
set "dir=C:\Your\Root\Directory"
set "pfx=Seekret file "
setlocal enabledelayedexpansion
for /r "%dir%" %%A in (*.txt) do (
set "txt=%%~nA"
if not "!txt:~0,13!"=="%pfx%" ren "%%A" "%pfx%%%~nxA"
)
pause
for /r will loop recursively through all .txt files, set each one as parameter %%A (per iteration), set a variable txt as parameter %%A reduced to just its name (%%~nA), and then it compares the first 13 characters of the text file to your example prefix (which is 13 characters long when you include the space: Seekret file) - if they match the loop does nothing; if they do not match, the loop will rename %%A to include the prefix at the beginning. If you don't want it to be recursive, you can use for %%A in ("%dir%"\*.txt) do ( instead. Other than that, you'll just change !txt:~0,13! depending on what your prefix is or how many letters into a filename you want to check. You also don't have to set your directory and prefix variables, I just prefer to do so because it makes the block look cleaner - and it's easier to go back and change one value as opposed to every place that value occurs in a script.
Reference: for /r, ren, variable substrings
I have a deployment directory that contains subdirectories, one for each deployment. I'm trying to write a batch script that, among other things, performs a cd into the newest one of these directories.
I know how to do this in bash (has already been ansered here as well), but I don't know how to accomplish the same thing in Windows cmd. Can anyone help me?
In a batch file following lines can be used to changed to the subdirectory with newest modification date:
#echo off
for /F "eol=| delims=" %%I in ('dir * /AD /B /O-D 2^>nul') do cd "%%I" & goto DoneCD
echo No subdirectory found in: "%CD%"
:DoneCD
The command FOR with option /F starts a new command process with %ComSpec% /c and the command line specified between ' as further arguments in background. So executed by FOR is with usual Windows installation path:
C:\Windows\System32\cmd.exe /c dir * /AD /B /O-D 2>nul
DIR executed by background command process searches with the specified arguments
in current directory
for directories because of option /AD (attribute directory)
matching the wildcard pattern * (all)
and outputs
in bare format because of option /B just the directory names without path never enclosed in "
ordered reverse by last modification date because of option /O-D and not using option /TC (creation date) or /TA (last access date) which means first the newest modified directory and last the oldest modified directory.
The output by DIR is written to handle STDOUT of the started background command process.
2>nul redirects the error message output by DIR on not finding any directory in current directory from handle STDERR to device NUL to suppress this error message.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures everything written by DIR to handle STDOUT of started command process and processes this output line by line after started cmd.exe terminated itself.
FOR ignores empty lines which do not occur here because of DIR outputs the list of directory names without empty lines because of using /B.
FOR would split up by default a line into substrings (tokens) using normal space and horizontal tab character as delimiters. After this substring splitting is done FOR would by default check if the first substring starts with default end of line character ; in which case the line would be ignored like an empty line. Otherwise FOR would assign first space/tab delimited string to the specified loop variable I and would execute the command line with CD and GOTO.
A directory name could be for example ;Test Folder, i.e. a directory name starting with a space and a semicolon and containing one more space. Such a directory name would be split up to ;Test (without space at beginning) and Folder and next ignored by FOR because of ;Test starts with a semicolon.
For that reason the end of line character is redefined from default semicolon to a vertical bar with eol=| which is a character no file or folder name can contain according to Microsoft documentation about Naming Files, Paths, and Namespaces. And line splitting behavior is disabled with delims= at end of options argument string after for /F which defines an empty list of delimiters. So the directory name as output by DIR is assigned to loop variable I without any modification even on being a very unusual name for a directory.
FOR executes command CD which changes current directory to the last modified subdirectory of the current directory and next command GOTO is executed to continue the processing of the batch file on the line below the label line :DoneCD. So the FOR loop execution is broken already after processing first directory name with command GOTO.
It is of course possible to use other commands after the FOR command line and the label line :DoneCD than just the ECHO line reporting that no subdirectory was found in current directory as shown by referencing dynamic environment variable CD like a command line to exit batch processing on this unusual use case or error condition case.
This FOR command line with the command GOTO to exit FOR loop after CD cannot be used in a Windows command prompt window. A solution for Windows command prompt window would be:
set "DoneCD=" & (#for /F "eol=| delims=" %I in ('dir * /AD /B /O-D 2^>nul') do #if not defined DoneCD cd "%I" & set "DoneCD=1") & set "DoneCD="
In a batch file this single line with multiple commands would be written as
#set "DoneCD=" & (#for /F "eol=| delims=" %%I in ('dir * /AD /B /O-D 2^>nul') do #if not defined DoneCD cd "%%I" & set "DoneCD=1") & set "DoneCD="
or better readable in its multi-line version with an additional echo as
#echo off
set "DoneCD="
for /F "eol=| delims=" %%I in ('dir * /AD /B /O-D 2^>nul') do (
if not defined DoneCD (
cd "%%I"
set "DoneCD=1"
)
)
if not defined DoneCD echo No subdirectory found in: "%CD%"
set "DoneCD="
First the environment variable DoneCD is deleted if it is defined by chance.
Next FOR runs cmd.exe with DIR as described above and processes the first output directory with newest modification date. The IF condition is true on newest directory as the environment variable was definitely undefined before execution of FOR. So command CD is executed to change the current directory to newest subdirectory. Then the environment variable DoneCD is defined with value 1. Any other value would be also possible like on using set "DoneCD=%%I". Important here is that for the other subdirectories output by DIR the environment variable DoneCD is now defined and so the IF condition is always false. So there is no attempt made to change in current subdirectory of initial current directory into a subdirectory not existing here or existing by chance also in the subdirectory.
Finally the environment variable DoneCD is deleted again if defined at all during execution of 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.
cd /?
dir /?
echo /?
for /?
goto /?
if /?
set /? ... explaining on last help page dynamic environment variable CD.
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.