这是我的批处理文件:
CD c:/
PAUSE
在 Windows PowerShell 中运行时,我得到以下输出:
PS C:\some\path\here> .\myscript.bat
C:\some\path\here>CD c:/
c:\>PAUSE
Press any key to continue . . .
PS C:\some\path\here>
程序终止后,我又回到了开始的地方!
同时,在cmd中运行时:
C:\some\path\here>.\myscript.bat
C:\some\path\here>CD c:/
c:\>PAUSE
Press any key to continue . . .
c:\>
我得到了我想要的行为,程序结束后我仍保留在
C:\
目录中。
为什么会出现这种情况?我怎样才能在 PowerShell 中获得这种行为?
从 PowerShell
运行批处理文件(
*.cmd
或
*.bat
)必然涉及 cmd.exe
子进程。 (PowerShell 本身不理解批处理文件,只有批处理文件解释器 cmd.exe
可以理解;作为单独的可执行文件,它总是在子进程中运行。)
子进程从根本上不能设置其父进程的工作目录(也不能修改其环境变量)。因此,无论批处理文件设置为其工作目录如何,对 PowerShell 调用者都没有影响。
因此,更改调用者的工作目录只能通过运行in-process的代码实现。
cmd.exe
和PowerShell都运行它们的脚本文件 - 分别是批处理文件(*.cmd
、*.bat
)和PowerShell脚本(*.ps1
) - in-process,使脚本文件能够更改进程的工作全局目录会话在其本机 shell 中 - 这是您从 cmd.exe
会话调用批处理文件时看到的内容。
在PowerShell中,您可以创建一个与批处理文件(在本例中)内容相同的*.ps1
文件,并调用它(例如
.\myscript.ps1
)将更改PowerShell会话的工作目录。
cmd.exe
中,您可以通过将批处理文件代码包含在
setlocal
和
endlocal
语句中来实现此目的(后者通常被省略,因为它在退出批处理文件时隐含),这不仅恢复了原始状态工作目录,还有原始环境变量。
没有类似机制,[1],因此必须手动保存和恢复之前的状态:
$oldPwd = $PWD
try {
# ... script code that changes the working dir. goes here
} finally { Set-Location -LiteralPath $oldPwd }
cd
) 调用最好在 PowerShell 脚本中避免。
[1] 但是,PowerShell 默认情况下将脚本中定义的范围其shell 变量(以及其他定义,例如函数)定义到该脚本。 Shell 变量(例如 $foo
)是 PowerShell 特定的变量,与环境 变量(在 PowerShell 中以前缀
$env:
引用)不同;修改后者确实会在全局进程中生效(并影响任何继承它们副本的子进程),并且 - 与批处理文件不同 - 没有作用域机制。请注意,
cmd.exe
使得 shell 变量和环境变量之间没有区别:定义的任何变量都是隐式环境变量。那是因为 *.bat 文件是
echo %var%
testenv.bat
echo %var%
最后一个命令打印出
inside
,这对大多数人来说是意想不到的
包括 PowerShell 在内的大多数其他 shell 中的正确行为是在子 shell 中运行脚本,除非使用显式
source
/.
命令。 PowerShell 中的等效项也是
dotsource 运算符,因此如果您运行 *.ps1 脚本,则像这样运行
. path\to\your\script.ps1
但是您正在 PowerShell 中运行 *.bat 脚本,因此显然您不能这样做,因为它适用于完全不同的 shell。简而言之:你不能那样做。子 shell 永远无法更改父 shell 的环境,这就是 为什么 cd 始终是所有 shell 中的内置命令