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:"..."
.