【发布时间】:2020-02-06 08:31:27
【问题描述】:
当我从任何文件夹(例如C:\Scripts)启动 PowerShell 脚本时,
脚本中的“当前”PowerShell 文件夹
始终是用户配置文件文件夹(例如 c:\users\Joe)
(用只有Get-Location的脚本测试)
但应该是启动脚本的文件夹...
我该如何解决这个问题?
【问题讨论】:
标签: powershell path scripting working-directory
当我从任何文件夹(例如C:\Scripts)启动 PowerShell 脚本时,
脚本中的“当前”PowerShell 文件夹
始终是用户配置文件文件夹(例如 c:\users\Joe)
(用只有Get-Location的脚本测试)
但应该是启动脚本的文件夹...
我该如何解决这个问题?
【问题讨论】:
标签: powershell path scripting working-directory
当 PowerShell 直接调用 *.ps1 脚本时,该脚本在与调用者相同的运行空间中运行进程中,并且因此默认情况下,脚本会看到 与调用者相同的当前位置(工作目录)。
顺便说一句:相反,如果脚本更改当前位置,调用者也会在脚本退出后看到这一点(详见下文)。
如果您没有看到此行为,则暗示某些幕后代码正在改变当前位置。
我可以真实地看到这种情况发生的唯一方法是在以下情况下:
如果 (a) 您的 $PROFILE 文件包含明确切换到您的主文件夹的 Set-Location / Push-Location 命令或 (b)(如您的情况,我们现在知道) 如果在自动加载模块中存在(自动)导入[1]和的自动加载模块中的代码:
您通过其CLI 调用 PowerShell(powershell.exe 用于 Windows PowerShell,pwsh 用于 PowerShell Core)没有 @987654332 @开关;例如powershell -File someScript.ps1
在类 Unix 平台上,您的脚本被实现并调用为带有 shebang 行的无扩展、可执行的 shell 脚本(不包括 -NoProfile - 详情见下文)。
如果您通过以下命令/功能之一运行脚本:
通过Start-Job(在子进程中)或Start-ThreadJob(在新的运行空间中),至少目前是(PowerShell Core 7.0.0-preview.4)
通过 PowerShell SDK,在新的运行空间中。
除此之外,我只能看到意外基于事件的代码产生您的症状,例如以下 - 毫无意义 - 重新定义交互式提示字符串定义函数 prompt,以使它在每个命令后悄悄地切换到$HOME 目录:
# !! Obviously, do NOT do this.
function prompt {
# Print the usual prompt string.
"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
# Quietly return to $HOME
Set-Location $HOME
}
答案的其余部分讨论了一个可能感兴趣的相关场景。
在 PowerShell v3+ 中,自动变量 $PSScriptRoot 包含执行脚本所在目录的完整路径。
如果您需要使用自己的目录作为工作目录来执行脚本(当前位置),请使用以下方法:
# Save the current location and switch to this script's directory.
# Note: This shouldn't fail; if it did, it would indicate a
# serious system-wide problem.
$prevPwd = $PWD; Set-Location -ErrorAction Stop -LiteralPath $PSScriptRoot
try {
# Your script's body here.
# ...
$PWD # output the current location
}
finally {
# Restore the previous location.
$prevPwd | Set-Location
}
注意:
显式恢复之前位置(目录)的原因是PowerShell运行脚本(.ps1文件)进程中,所以如果一个脚本用Set-Location或Push-Location改变当前位置,它生效session-globally;也就是说,即使在脚本退出后,新位置仍然存在。
在 类 Unix 平台(Linux、macOS)上,使用 PowerShell Core,您现在可以选择创建(无扩展)可执行 shell 脚本shebang line;此类脚本在子进程中运行,因此没有需要恢复之前的位置(目录) .
$PSScriptRoot)被损坏,如 @987654324 中所述@。[1] 任何模块都不应该在导入时更改会话全局状态(除了导入其命令),但从技术上讲可能,即如果您放置诸如 @ 之类的命令987654346@ 在脚本模块的 *.psm1 文件的顶级范围内或在通过 ScriptsToProcess 模块清单条目在调用者范围内运行的脚本中。甚至二进制 cmdlet 也可以在导入时通过 System.Management.Automation.IModuleAssemblyInitializer 接口执行代码 - 请参阅 this answer。
【讨论】: