The following illustrates a problem I found using for /f %l in ('<command>') do @(echo %l). (/f is the for command's parameter for "iterating and file parsing.") This works as expected when <command> is cd or chdir, but not when <command> is pushd:
C:\>cd
C:\
C:\>for /f %l in ('cd') do @(echo %l)
C:\
C:\>chdir
C:\
C:\>for /f %l in ('chdir') do @(echo %l)
C:\
C:\>pushd Windows
C:\Windows>pushd
C:\
C:\Windows>for /f %l in ('pushd') do @(echo %l)
C:\Windows>pushd > nul
C:\Windows>
I would expect the last command to print C:\ but it does not execute the do block at all. I added pushd > nul to check that pushd prints to stdout.
Information about command processing by FOR /F
The usage of a
for /Floop with a command enclosed in'or in`on usingusebackqresults in starting in background one more command process using%ComSpec% /cand the command appended as additional argument(s).The usage of
for /f %l in ('pushd') do @(echo %l)results in background execution of:That can be seen by downloading, extracting and running the free Windows Sysinternals tool Process Monitor as administrator which logs the execution of two
cmd.exeprocesses with different process identifiers on running the commands as posted in the question. There must be double clicked on any line in log of Process Monitor of secondcmd.exein the middle of the log to open the Event Properties window and selected the second tab Process to see the Command Line which was used by firstcmd.exeto start the secondcmd.exefor the execution of the commandpushd.There cannot be executed just a single command with a
for /Floop. There can be executed an entire command line which can even have multiple commands. But it is necessary to take into account all the information given by the usage help ofcmdoutput on runningcmd /?in a command prompt window on using a complex command line withfor /Fand processing its output as written to handle STDOUT of in background startedcmd.exe.Command operators like
&,&&and||as well as redirection operators like|,2>and2>&1in the command line to execute byfor /Fare processed by twocmd.exe, first the one executing the entirefor /Floop and another one started in background with the command line of which output is of interest. That is the reason why manyfor /Floops with a complex command line are with the escape character^left to each&and|and>in the command line to get these characters interpreted literally bycmd.exeparsing and executing the entirefor /Floop while being interpreted as command/redirection operators by the secondcmd.exestarted in background which is really executing the command line.Information about output of PUSHD
The Windows command PUSHD outputs on execution without any directory path the list of directory paths pushed on stack of current command process.
There can be executed in a command prompt window following commands:
The fifth command
pushdresults usually for typical Windows installations in the output:But there is nothing output by using as fifth command instead of
pushdthe command line:The reason is the execution of
pushdby one morecmd.exestarted in background which has no directory paths pushed on its stack. The internal command PUSHD does not output anything at all for that reason to handle STDOUT of in background startedcmd.exe.The command process running
for /Fcannot capture any output text. Thefor /Floop cannot process therefore any line aftercmd.exestarted in background finished the execution ofpushdand closed itself.How to process the directory paths pushed on stack?
It would be necessary to use the following command lines in the command prompt window to process the directory paths pushed on stack of the current command process:
The redirection of the output of PUSHD executed by the command process which executed also the two
pushdcommand lines before into a temporary file makes it possible to process the directory paths pushed on stack of current command process.The
for /Foptionusebackqis necessary to get the string inside"interpreted as file name of which lines to process by the FOR loop and not as string to process.The
for /Foptiondelims=is necessary to define an empty list of string delimiters as a directory path can contain one or more spaces. There is by default split up a line into substrings using normal space and horizontal tab as string delimiters and assigned to the loop variable is just the first space/tab delimited string instead of the entire directory path. The line splitting is turned off by the definition of an empty list of delimiters. The entire directory path is assigned therefore always to the loop variable in this case with processing always full directory paths never starting with the default end of line character;as the first character is always either a drive letter or a backslash in case of a UNC directory path.Why does the command CD work with a FOR /F loop?
The command CD works also with
for /F "delims=" %I in ('cd') do @echo %Ibecause ofcmd.execalls the Windows kernel library function CreateProcess on starting the additional command process for execution of a command specified as set of afor /Floop with value NULL for the function parameterlpCurrentDirectory. The current directory of in background startedcmd.exeis set byCreateProcessfor that reason with the current directory of the command process executing thefor /Fcommand line. Both running command processes have the same current directory during the execution of the command of afor /Floop.Information about environment variable ComSpec
ComSpecis an environment variable defined by default with%SystemRoot%\system32\cmd.exe(withsat beginning ofSystem32instead ofSas the folder name is in real by default) as system environment variable stored in Windows registry under the keyHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment.It is really not advisable to ever modify or even delete the environment variable
ComSpecneither in local environment of a running process nor in the Windows registry. That would cause a lot of programs to stop working normally as lots of executables depend internally on the correct definition of this environment variable includingcmd.exeitself.The reason is that many applications and scripts use internally the function system which uses the environment variable
ComSpecto start on Windowscmd.exefor execution of a command line.There are many batch files running internal command
verofcmd.exeto get the Windows version like:That works only for Windows NT based Windows versions using
cmd.exeas command processor and not for older Windows versions usingCOMMAND.COMlike Windows 95/98/ME for processing a batch file. It works only with enabled command extensions which are enabled by Windows default, but can be disabled by commandsetlocal DisableExtensionin a batch file, on startingcmd.exewith/E:OFFor by a registry value which should be really never used and therefore not written here. The commandvermust be really executed by%SystemRoot%\System32\cmd.exebecause of in real is output the version ofcmd.exeand not the version of Windows.However, that command line is very good to demonstrate what happens on usage of
for /Fif the environment variableComSpecis not defined in environment ofcmd.exeon running the batch file with the twofor /Floops.ComSpec usage by cmd.exe on Windows XP
On Windows XP is always called
C:\WINDOWS\system32\cmd.exe /c vereven on doing following:cmd.exeto the directoryF:\Temp\system32.F:\Temp\system32\cmd.exewith a double click in Windows Explorer.set ComSpec=F:\Temp\system32\cmd.exeandset SystemRoot=F:\Tempandset SystemDrive=F:andset windir=F:\Temp.echo %__APPDIR__%thatF:\Temp\system32\is output.That can be seen with Process Monitor v3.61 on Windows XP x86.
There can be even modified the local environment variable
PATHto begin withF:\Temp\system32instead ofC:\WINDOWS\system32or the local environment variableComSpecis deleted withset ComSpec=. The Windows Command Processor of Windows XP in directoryF:\Temp\system32calls nevertheless alwaysC:\WINDOWS\system32\cmd.exewith the option/cand the commandveron running the batch file immediately after closing the batch file containing just the single command line in the text editor.There cannot be seen in Process Monitor even a Windows registry access by
cmd.exeof Windows XP to get any directory path.Please read further why
cmd.exeof Windows XP in a different directory than%SystemRoot%\System32\cmd.execalls nevertheless the command processor executable in Windows system directory even after modification of local environment variableComSpecor its deletion.ComSpec usage of cmd.exe on Windows Vista/7/8/8.1/10/11
The same procedure as described above for Windows XP can be executed also on Windows 7 x64 and newer 64-bit Windows versions.
The double clicked 64-bit
F:\Temp\system32\cmd.execopied fromC:\Windows\System32outputs on Windows 7 and currently latest Windows 11 22H2 first:Note: The copyright message line depends on version of
cmd.exe. The output above is fromcmd.exeof version 10.0.22621.963 (Windows 11 22H2).The Windows Command Processor
cmd.exeof version 6.1.7601 of Windows 7 outputs additionally the line:The execution of the internal command
verexecuted in the command prompt window opened with a double click onF:\Temp\System32\cmd.exefails on Windows 7 and all later Windows versions up to currently latest Windows 11 22H2 which is the reason for the strange output on startingF:\Temp\System32\cmd.exewith a double click.There can be redefined also the environment variables as described above and done on German Windows XP by me on my tests. A verification of the output of
echo %__APPDIR__%works and showsF:\Temp\system32\as expected. So, it works to access the string value of the internal dynamic variable ofcmd.exeeven on executedcmd.exeis not in the directory%SystemRoot%\System32.But the batch file execution from within the command prompt of
F:\Temp\system32\cmd.exeas on Windows XP results in no output at all.In log of Process Monitor v3.70 or a newer Process Monitor version can be seen that on Windows 7 and Windows 11 22H2 is executed
F:\Temp\System32\cmd.exe /c ver. That means it is indeed possible that anothercmd.exeis executed instead ofC:\Windows\System32\cmd.exeon Windows 7 and newer Windows versions.But why is no version output?
Well, the execution of
verresults in the output of the error message:64-bit
cmd.exeis not working inF:\Temp\system32at all.The usage of 32-bit
cmd.execopied fromC:\Windows\SysWOW64toF:\Temp\system32makes no difference. The same error messages are output already on startingF:\Temp\system32\cmd.exeand on running nextveror the batch file after modification of local environment variableComSpec.Conclusion:
cmd.exeof Windows Vista and newer versions are not fully working on being stored outside the appropriate system directory%SystemRoot%\System32or%SystemRoot%\SysWOW64whilecmd.exeof Windows XP works fine in any directory.Caching of ComSpec value
I found out with lots of further tests that the string value of the environment variable
ComSpecis indeed used to findcmd.exeto run commandveron execution of the batch file with thefor /Floop. But there is a caching mechanism on Windows Vista and newer Windows versions.There must be run immediately
set ComSpec=F:\Temp\system32\cmd.exeafter startingF:\Temp\system32\cmd.exebefore running the batch file. This results in callingF:\Temp\system32\cmd.exe /c ver. If there is next executedset ComSpec=C:\Windows\System32\cmd.exeto redefine the variable with correct value and run the batch file once again, there is nevertheless run now not workingF:\Temp\system32\cmd.exe /c ver.The same caching mechanism can be seen on starting
F:\Temp\system32\cmd.exe, then running the batch file resulting in calling in backgroundC:\Windows\System32\cmd.exe /c ver, next runningset ComSpec=F:\Temp\system32\cmd.exeand running now the batch file again. There is executed once againC:\Windows\System32\cmd.exe /c veralthough the value of the environment variableComSpecis nowF:\Temp\system32\cmd.exe.It looks like
cmd.exeof Windows Vista/7/8/8.1/10/11 reads the value ofComSpeconly once and keeps its value in memory for further usage without reading the environment variable a second time on string value for the default command interpreter being already in its internal memory.That is quite clever in my opinion. The string value of
ComSpecis read only once from the environment variable on first usage and then is used that string internally on further usage because of the value of the environment variableComSpecchanges usually never as long ascmd.exeis running.It looks like
cmd.exeof Windows XP has nearly the same caching mechanism for the string value of the environment variableComSpec. The difference is thatcmd.exeofWindows XPreads the value ofComSpecalready on starting it and not on first usage and so all changes done on local environment variableComSpecof an already runningcmd.exehave no effect on the execution of one morecmd.exeon processing afor /Floop with a command line to execute, capturing the output and processing it as I could find out with further tests on German Windows XP.