批处理中的PowerShell:使用FOR循环和Tmp变量意外创建目录:-O

问题描述 投票:0回答:1

我必须升级现有的批处理脚本以满足新的需求。这个正在启动 PowerShell 下标。我发现了一个意想不到的行为,我可以在较小的用例上重现它。

创建一个“测试”目录 在里面,创建2个目录“1”和“2” 在目录“1”中,创建2个空文件“1.txt”和“2.txt” 在目录“2”中,创建2个空文件“3.txt”和“4.txt” 在“test”中,使用以下代码创建text.bat:

@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

运行批处理时,files.txt 和 files2.txt 按预期创建为“1”和“2”。 “1”的文件按预期打印出来。 但在第二个 Powershell 脚本之后,会在文件夹“2”中创建一个名为“files.txt”的文件夹。 我用 Tmp 变量的其他名称进行了一些测试,奇怪的是 pb 没有出现具有不同名称的变量(例如“Tmp2”)

有人可以解释这种奇怪的行为吗?

我使用的是 Windows 10, PS版本5.1.19041.4046 PS版桌面版 PS兼容版本 {1.0、2.0、3.0、4.0...} 构建版本 10.0.19041.4046 CLR版本 4.0.30319.42000 WSManStack版本3.0 PSRemoting协议版本2.3 序列化版本1.1.0.1

batch-file powershell-5.1
1个回答
0
投票

user环境变量

TEMP
TMP
由Windows默认定义为临时文件目录的路径。打开 命令提示符 窗口并运行
set TEMP
set TMP
以查看它们及其值。只需运行
set
即可查看当前用户帐户默认定义的所有环境变量,另请参阅 Windows 环境变量

在命令提示符窗口中运行

reg QUERY HKCU\Environment
,它们的输出最有可能至少是:

HKEY_CURRENT_USER\Environment
    TEMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
    TMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp

批处理文件中的命令行

set Tmp=%%b
FOR循环中重新定义local环境变量
TMP
,其中包含从文件
files.txt
读取的当前文件的名称,该文件是在最后一个循环运行字符串
 files.txt

这就是问题的根源。重新定义环境变量

TMP
,根据定义保存带有字符串
files.txt
的临时文件目录的完整路径,这并不是一个好主意。

可以使用免费的 Windows Sysinternals (Microsoft) 工具 Process Monitor 查看由

powershell.exe
第二次运行时
cmd.exe
完成的所有注册表和文件系统访问。

我建议更改命令行

PowerShell "Get-ChildItem -Recurse | Select-Object -ExpandProperty Name | Out-File -FilePath files2.txt -Encoding ASCII"

在批处理文件中

PowerShell "echo hello"

这使得您可以更轻松地在 Process Monitor 的日志中查看后台发生的情况。

或者使用以下单行批处理文件来重现问题:

@setlocal & set "TMP=files.txt" & %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe "echo hello" & endlocal

除了默认值之外,在Process Monitor中设置的推荐过滤器:

专栏 关系 价值 行动
进程名称 cmd.exe 包括
进程名称 powershell.exe 包括

PowerShell想要在临时文件目录中创建一个小脚本文件

__PSScriptPolicyTest_*.*.ps1
,并将其目录路径分配给环境变量
TMP
。这应该是在
FOR
循环中最后一次重新定义环境变量
files.txt
后当前工作目录中的目录 TMP 分别作为单行批处理文件中的第二个命令。当前目录中当前不存在目录
files.txt
。但这对于 PowerShell 来说不是问题,因为它现在成功创建了目录
files.txt

然后在当前工作目录的临时文件目录

__PSScriptPolicyTest_*.*.ps1
中创建脚本文件
files.txt

通过Process Monitor可以看到,每次执行PowerShell都会导致大量的注册表和文件系统访问,甚至在执行像

echo hello
这样的简单PS命令之前。

我还建议阅读 “X 不被识别为内部或外部命令、可操作程序或批处理文件”的原因是什么? 环境变量在这里

TMP
而不是
PATH
cmd.exe
调用Windows 内核库函数 CreateProcess 用于执行
powershell.exe
,函数参数
lpEnvironment
为空指针。因此,Windows 内核函数
CreateProcess
使用进程
cmd.exe
重新定义的
TMP
变量复制进程
powershell.exe
的当前环境变量列表,并且现在使用错误的临时文件目录完成 PowerShell 的标准操作。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.