I have to upgrade an existing batch script, for a new need. This one is launching PowerShell subscripts. I found an unexpected behaviour, that I can reproduce on a smaller use case.
Create a "test" directory Inside, create 2 directories "1" and "2" In directory "1", create 2 empty files, "1.txt" and "2.txt" In directory "2", create 2 empty files, "3.txt" and "4.txt" In "test", create text.bat with the following code:
@echo OFF
SETLOCAL EnableDelayedExpansion
REM go to directory "1" and get file names into files.txt
chdir 1
PowerShell "Get-ChildItem -Recurse | Select-Object -ExpandProperty Name | Out-File -FilePath files.txt -Encoding ASCII"
REM loop over file names and print them
for /F "usebackq tokens=* " %%b in ("files.txt") do (
REM set a variable to each file name and print it
set Tmp=%%b
echo !Tmp!
)
REM go to directory "2" and get file names into files.txt
chdir ..
chdir 2
pause
REM At this stage, nothing strange in folder "2"
PowerShell "Get-ChildItem -Recurse | Select-Object -ExpandProperty Name | Out-File -FilePath files2.txt -Encoding ASCII"
REM At this stage, in directory "2", files2.txt is created correctly. BUT a unexpected DIRECTORY has been created, which name if the one of the last looped file in directory "1" (in that case, "files.txt")
pause
When running the batch, files.txt and files2.txt are created into "1" and "2", as expected. The files of "1" are printed out as expected. BUT right after the second Powershell script, a folder, named "files.txt" is created into folder "2". I made some test with other names for the Tmp variable, and oddly the pb does not appear with a variable named differently ("Tmp2" for example)
Can someone explain this strange behaviour ?
I'm on Windows 10, with PSVersion 5.1.19041.4046 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.19041.4046 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
The user environment variables
TEMPandTMPare defined by Windows default with the path of the directory for temporary files. Open a command prompt window and runset TEMPandset TMPto see them with their values. Run justsetto see all environment variables defined by default for the current user account and see also Windows environment variables.Run in the command prompt window
reg QUERY HKCU\Environmentand their is output most likely at least:The command line
set Tmp=%%bin the batch file redefines in the FOR loop the local environment variableTMPwith the name of the current file read from the filefiles.txtwhich is on last loop run the stringfiles.txt.That is the source of the problem. The redefinition of the environment variable
TMPholding per definition the full path to the directory for temporary files with the stringfiles.txtis no good idea.There can be viewed with the free Windows Sysinternals (Microsoft) tool Process Monitor all the registry and file system accesses done by
powershell.exeon being run bycmd.exea second time.I recommend to change the command line
in the batch file to just
That makes it easier to see in log of Process Monitor what happens in the background.
Or there is used following single line batch file for reproducing the issue:
Recommended filters set in Process Monitor in addition to the defaults:
PowerShell wants to create a small script file
__PSScriptPolicyTest_*.*.ps1in the directory for temporary files of which directory path is assigned to the environment variableTMP. That should be the directoryfiles.txtin the current working directory after the last redefinition of the environment variableTMPin the FOR loop respectively as second command in the single line batch file. The directoryfiles.txtdoes currently not exist in the current directory. But that is no problem for PowerShell as it creates now successfully the directoryfiles.txt.Then the script file
__PSScriptPolicyTest_*.*.ps1is created in the temporary files directoryfiles.txtin the current working directory.It can be seen with Process Monitor that each execution of PowerShell causes lots of registry and file system accesses before even a simple PS command like
echo hellois executed.I recommend reading also What is the reason for "X is not recognized as an internal or external command, operable program or batch file"? The environment variable is here
TMPand notPATHbutcmd.execalls the Windows kernel library function CreateProcess for execution ofpowershell.exewith a null pointer for the function parameterlpEnvironment. The Windows kernel functionCreateProcessmakes therefore a copy of the current environment variables list of processcmd.exewith the redefinedTMPvariable for processpowershell.exeand standard actions of PowerShell are done now with wrong temporary files directory.