I'm using the following commands:
C:\>for %I in (a: b: c: ">:" "&:") do @rem %~fI
C:\>pushd c:
C:\>set "
and the output:
=&:=&:\
=>:=>:\
=A:=A:\
=B:=B:\
=C:=C:\
....
As the =Drive:
variables are storing the last accessed path the corresponding drive , it looks like the %~fI
expansion somehow accessed not existing drive (which is not possible) . (all parameter expansions create such variables)
I think there are two things contributing:
A drive letter can actually be every character other than white-spaces,
/
and\
. Check out thesubst
command, which accepts also an&
, for example (although it is not listed bysubst
):for
does not access the file system unless it really needs to, which is the case when:?
,*
) are used in the set (where the file system needs to be accessed by thefor
command immediately);the
for
reference (%I
) expansion requires information from the file system:~s
,~a
,~t
,~z
and~$ENV:
, information from the file system is required, of course;~n
,~x
and~p
, and also the corresponding parts of~f
, which is nothing but~dpnx
, the file system is accessed for case preservation (if the path does not exist, the original case is maintained);~n
,~x
,~p
and~d
, and also~f
, the file system needs to be accessed in case a relative path is provided, with or without a dedicated drive specified (for instance,abc\def
,D:data
,P:
), because the current working directory path (of the given drive in case) needs to be determined;So the
~d
modifier, and also the corresponding part of~f
, is handled by pure string manipulation as long as the file system is not accessed according to the aforementioned conditions.Simply try your original code but with absolute paths, like
for %I in (a:\ b:\ c:\ ">:\" "&:\") do @rem %~fI
, etc., and you will find that there are no corresponding=Drive:
variables.Summary
Drive letters can literally be almost any characters (see
subst
). As soon asfor
accesses the file system to search for drives and paths, the accessed drives are recorded in the=Drive:
variables.IMO the drives aren't really accessed but parsed at an early stage.
A simple for loop can be used to parse nonexistent drives\pathes\files
The very last one is quite interesting
Windows command interpreter tries to get real name of a file or directory on storage media on using an extension like
%~fI
by processing aQueryDirectory
as it can be seen on using Sysinternals Process Monitor. But the loop variableI
just holds a string value as defined in set.From a programmers point of view what should the command interpreter do for example on following code?
There is most likely no file with name
#abcdefghi.tmp
in directory for temporary files for system processes. But output is neverthelessThe Windows command interpreter must built a string as the batch code expects a string. It can't replace
%%~fI
with nothing or with an error message text as this would definitely result in an undefined behavior on further processing of the command lines in the batch file.Exiting batch processing completely is also no option for Windows command interpreter because the FOR loop could be used to check for existence of files.
So the Windows command interpreter makes its best to build from current directory and current string of loop variable a valid file name with path, or just file path, or just file name, ... independent on existence of file/directory or validity of created string.
The command line user respectively writer of batch code has to check for validity or existence and not Windows command interpreter on expanding loop variable reference.
When a modifier is used in the
for
replaceable parameter to request a path element, thefor
command (well, a function that retrieves the contents of the variables being read) uses theGetFullPathName
function to adapt the input string to something that could be handled. This API function (well, some of the OS base functions called by this API) generates the indicated behaviour when a relative path is requested. You can test this c code (sorry, just a quick code test), calling the executable with the ex.;:
as the first argument.to get something like
EDITED 2016/12/23
This is for windows 10, but as windows 7 behaves the same it should share the same or similar code.
The output of environment strings to console is handled by
DisplayEnvVariable
function. In older windows versions (checked and XP did it this way) this function callsGetEnvironmentStrings
to retrive the values, but now (checked and in Vista it was changed) a pointer to a memory area is used. Somehow (sorry, at this moment I can not give this problem more time), it points to a non updated copy of the environment (in this case the updated was not generated bycmd
command, but from a baseRtl
function called when resolving the current drive path), generating the observed behaviour.It is not necessary to execute a
pushd
orcd
command, any change to the environment or any process creation will result in an update of the pointer.You can replace the
more
line with a simpleset "thisIsNotSet="
to get the same result