PowerShell 脚本工作目录(当前位置)

PowerShell script working directory (current location)

当我从任何文件夹(例如 C:\Scripts)启动 PowerShell 脚本时, 脚本中的 'current' PowerShell 文件夹 始终是用户配置文件文件夹(例如 c:\users\Joe) (用只有 Get-Location 的脚本测试)

但应该是启动脚本的文件夹...

我该如何解决这个问题?

当 PowerShell 直接 调用 *.ps1 脚本 时,该脚本 运行s 进程中,在与调用者相同的运行空间中,因此脚本默认看到相同的当前位置(工作目录)作为调用者.

顺便说一句:相反,如果脚本 更改了 当前位置,调用者也会在脚本退出后看到(详情见下文)。

如果你没有看到这个行为,这意味着一些幕后代码是更改当前位置。


我能真实地看到这种情况发生的唯一方法是在以下场景中:

  • 如果 (a) 您的 $PROFILE 文件包含明确切换到主文件夹的 Set-Location / Push-Location 命令 (b)(如您的情况,我们现在知道)如果自动加载模块中的代码损坏(自动) import[1] :

    • 您通过其 CLI 调用 PowerShell(powershell.exe 用于 Windows PowerShellpwsh 用于PowerShell Core) 没有 -NoProfile 开关;例如powershell -File someScript.ps1

    • 在类 Unix 平台上,您的脚本被实现并调用为无扩展的可执行 shell 脚本,带有 shebang 行(不包括 -NoProfile - 详情见下文)。

  • 如果您通过以下命令/功能之一运行您的脚本:

    • 通过Start-Job(在子进程中)或Start-ThreadJob(在新的运行空间中),至少目前(PowerShell Core 7.0.0-预览.4)

      • 注意:这两个 cmdlet 的行为并不完全相同,这本身就是有问题的,但更大的问题是它们不简单地继承 caller 的 当前位置 - 请参阅 this GitHub issue.
    • 通过 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 脚本:

在 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 运行s 脚本(.ps1 文件)在-process,因此如果脚本使用 Set-LocationPush-Location 更改当前位置,它 全局生效会话;也就是说,即使在脚本退出后新位置仍然存在。

  • 类 Unix 平台(Linux、macOS)上,使用 PowerShell Core,你现在可以选择使用 创建(无扩展)可执行 shell 脚本;这样的脚本 运行 在 子进程 中,因此 no需要恢复之前的位置(目录).

    • 不幸的是,从 PowerShell Core 7.0.0 预览版开始,访问有关脚本自身调用的信息(包括 $PSScriptRoot)在基于 shebang 的脚本中已损坏。 4,如 this GitHub issue.
    • 中所讨论

[1] 任何模块都不应在导入时更改会话全局状态(除了导入其命令),但技术上 可能 ,即如果您将 Set-Location 之类的命令放在脚本模块 *.psm1 文件的顶级范围内,或者通过 ScriptsToProcess 模块将 运行 放在调用者范围内的脚本中-清单条目。即使是二进制 cmdlet 也可以在导入时通过 System.Management.Automation.IModuleAssemblyInitializer 接口执行代码 - 请参阅 .