PowerShell 核心相对路径问题

问题描述

PowerShell:

$file = "text.txt"
Test-Path $file            
# True
 
Get-Location
# C:\Users\Fors1k

[IO.File]::OpenRead($file) 
# FileStream
 
[IO.FileInfo]::new($file)  
# Mode                 LastWriteTime         Length Name                         
# ----                 -------------         ------ ----                         
# -a----        05.03.2021     18:12              7 text.txt  
                      
gc $file                   
# test123

一切正常。

PowerShell 核心:

$file = "text.txt"
Test-Path $file            
# True
 
Get-Location
# C:\Users\Fors1k

[IO.File]::OpenRead($file) 
# Exception calling "OpenRead" with "1" argument(s): "Could not find file 'C:\Windows\system32\text.txt'."
 
[IO.FileInfo]::new($file)  
# Mode                 LastWriteTime         Length Name                         
# ----                 -------------         ------ ----
# larhs          01.01.1601     3:00  
                      
gc $file                   
# test123

这里我有错误

据我所知,问题是 .NET 方法认为,当前位置是 sysfolder,而不是真正的当前位置。

回应建议:

我知道有很多解决方案:

$file = "text.txt"
[Environment]::CurrentDirectory = Get-Location
[IO.File]::OpenRead($file)
#
#
$file = "text.txt"
[IO.Directory]::SetCurrentDirectory((Get-Location))
[IO.File]::OpenRead($file)
#
#
$file = "text.txt"
$file = Convert-Path $file
[IO.File]::OpenRead($file)
#
#
# My solution,before asking,was
$file = "text.txt"
$file = Get-Item $file
[IO.File]::OpenRead($file)

但所有这些都是“随时随地修复”(我不知道这个短语的正确英语习语)。

我的意思是,为什么这在 posh 5.1 上正常工作?

为什么这个代码[IO.File]::OpenRead("text.txt") 在我的朋友的 pwsh 上有效,我问的是哪些?

也许还有这样的设定(夸张):[DotNet.Path]::SetDefaultLocation("TheSameAsPwsh")

@mathias-r-jessen,明白你的意思:

[Environment]::CurrentDirectoryGet-Location 最初是不相关的东西。

PowerShell:

"$(Get-Location)" -eq ([Environment]::CurrentDirectory)
# True
##
##
Set-Location ([Environment]::GetFolderPath("desktop"))

"$(Get-Location)" -eq ([Environment]::CurrentDirectory)
# False (!!!)

所以,我的问题是了解正在发生的事情:

为什么我的 pwsh 的 [Environment]::CurrentDirectory 为 C:\Windows\system32,是否可以将此值更改为用户文件夹,就像它在 posh 中一样?

解决方法

更改 .NET 的当前工作目录。

[Environment]::CurrentDirectory = "$(Get-Location)"

[System.IO.Directory]::SetCurrentDirectory("$(Get-Location)")

替代代码 - 结果相同
您的代码使用流。您应该在最后释放/关闭流资源,并使用 try -finally。

$file = "text.txt"


if (! (Test-Path $file -PathType Leaf)) {
    New-Item $file
}

Get-Content $file

File.OpenRead(String path) Method
备注
允许路径参数指定相对或绝对路径信息。相对路径信息被解释为相对于当前工作目录。要获取当前工作目录,请参阅 GetCurrentDirectory

Directory.GetCurrentDirectory Method
备注
当前目录与原始目录不同,原始目录是启动进程的目录。

在 pwsh.exe 和 powershell.exe 中检查此方法。返回的值不同。我认为每个都有不同的实现。 pwsh Windows PowerShell

如果您从脚本文件运行代码。即 ./Script.ps1 您可以使用 $PSScriptroot 全局变量,它包含运行脚本的目录的路径。

Write-Host "ScriptPath: $PSScriptroot"
[Environment]::CurrentDirectory = "$PSScriptroot"
Write-Host "CurrentDirectory: $([Environment]::CurrentDirectory)"

pwsh

,

在 PowerShell 中更改位置不会自动更新进程的工作目录 - 这是 .NET 用于在内部解析相对路径的内容。

作为 Joma's answer excellently suggests,您可以通过在调用方法之前更新 [Environment]::CurrentDirectory 来解决此问题。

如果您发现自己在交互式使用 PowerShell 时经常遇到此问题,请添加一种机制来将其更新为 your prompt(以下示例直接从我自己的 profile scripts 中提取) - 这将保持两个“同步”:

$function:prompt = {
  if($PWD.Provider -eq 'FileSystem'){
    [Environment]::CurrentDirectory = $PWD.Path
  }

  "PS $($executionContext.SessionState.Path.CurrentLocation -replace [regex]::Escape($HOME),'~')$('>' * ($nestedPromptLevel + 1)) "
}