Как PowerShell обрабатывает "." в дорожках?
Рассмотрим следующую последовательность команд при открытии терминала PowerShell:
PS C:\Users\username> cd source
PS C:\Users\username\source> $dir = ".\temp"
PS C:\Users\username\source> [System.IO.Path]::GetFullPath($dir)
C:\Users\username\temp
Теперь это:
PS C:\Users\username> cd source
PS C:\Users\username\source> powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\username\source> $dir = ".\temp"
PS C:\Users\username\source> [System.IO.Path]::GetFullPath($dir)
C:\Users\username\source\temp
Почему PowerShell интерпретирует "." относительно каталога, в котором был запущен PowerShell, а не текущего каталога? Как я могу заставить его интерпретировать "." относительно текущего каталога?
3 ответа
Как js2010 полезно ответа государств, это использование метода.NET, что создает проблему:
.NET в единый, процесс в масштабах текущего каталога, как правило, и по дизайну[1] отличается от PowerShell в пространство выполнения конкретных один.
Это имеет следующие последствия:
Поскольку PowerShell сама делает надежно интерпретировать
.
в качестве текущего местоположения (которое является обобщением PowerShell концепции текущего каталога, который может относиться также к другим типам местоположений, на дисках, предоставляемых другими поставщиками дисков PowerShell, такими как поставщик реестра), вы можете избежать проблемы, используя Команды PowerShell, если они доступны.При вызове методов.NET не забудьте заранее преобразовать все относительные пути в абсолютные или, если это поддерживается, дополнительно указать текущее расположение файловой системы PowerShell в качестве справочного каталога - это позволяет избежать проблемы несовпадения текущих каталогов.
- (Другой, но неоптимальный вариант - сначала установить
[Environment]::CurrentDirectory = $PWD.ProviderPath
каждый раз, когда передается относительный путь, но это неуклюже и не должно использоваться, если есть вероятность, что в одном процессе существует несколько пространств выполнения PowerShell.)
- (Другой, но неоптимальный вариант - сначала установить
В следующем разделе показано, как безопасно передавать относительные пути PowerShell к методам.NET, тогда как нижний раздел решает конкретную проблему в вашем вопросе: как разрешить произвольный заданный путь PowerShell к абсолютному, собственному пути файловой системы.
Безопасная передача известного относительного пути PowerShell в метод.NET:
Как уже говорилось, несоответствие в текущих каталогах требует, чтобы методам.NET был передан абсолютный путь, полученный на основе текущего каталога PowerShell.
Примеры предполагают относительный путь someFile.txt
для передачи в метод.NET [IO.File]::ReadAllText()
Обратите внимание, что используется простая строковая интерполяция с /
(который может использоваться взаимозаменяемо с \
) используется для соединения компонентов пути; если текущий каталог является корневым каталогом, вы получите два разделителя пути, но это не повлияет на функциональность. Однако, если вам все же нужно избежать этого, используйтеJoin-Path
командлет вместо этого.
- Самый простой, но не полностью надежный, через
$PWD
(не выполняется, если текущий каталог основан на диске PowerShell, созданном с помощьюNew-PsDrive
или если текущее местоположение не является местоположением файловой системы):
[IO.File]::ReadAllText("$PWD/someFile.txt")
- Более надежный: через
$PWD.ProviderPath
(разрешает путь на основе диска PowerShell к базовому пути собственной файловой системы, но все равно может завершиться ошибкой, если текущее местоположение не является местоположением файловой системы):
[IO.File]::ReadAllText("$($PWD.ProviderPath)/someFile.txt")
- Полностью прочный: через
(Get-Location -PSProvider FileSystem).ProviderPath
[IO.File]::ReadAllText("$((Get-Location -PSProvider FileSystem).ProviderPath)/someFile.txt")
Примечание. Приведенное выше работает как с существующими, так и с несуществующими путями; если известно, что путь существует - например, с[IO.File]::ReadAllText()
, в отличие от [IO.File]::WriteAllText()
- вы также можете использовать следующее, но только если вы дополнительно предполагаете, что текущее местоположение является местоположением файловой системы:
[IO.File]::ReadAllText((Convert-Path -LiteralPath someFile.txt))
Что Convert-Path
а также Resolve-Path
работать только с существующими путями (начиная с PowerShell Core 7.0.0-preview.3) неудачно; предоставление согласия на несуществующий путь было предложено на GitHub.
Точно так же было бы полезно, если бы Convert-Path
а также Resolve-Path
поддержал -PSProvider
параметр, позволяющий явно указать целевого поставщика, как Get-Location
уже поддерживает - см. это предложение на GitHub.
Преобразование заданного произвольного пути файловой системы PowerShell в абсолютный собственный путь:
Если путь существует, используйтеConvert-Path
чтобы преобразовать любой путь к файловой системе PowerShell в абсолютный, родной для файловой системы:
$dir = "./temp"
Convert-Path -LiteralPath $dir
Связанные Resolve-Path
командлет предоставляет аналогичные функции, но не разрешает пути на основе дисков, специфичных для PowerShell (созданных с помощьюNew-PsDrive
) к их исходным путям файловой системы.
Если путь не существует (пока):
В PowerShell Core, построенном на.NET Core, вы можете использовать новый[IO.Path]::GetFullPath()
перегрузка, которая принимает справочный каталог для указанного относительного пути:
$dir = "./temp"
[IO.Path]::GetFullPath($dir, $PWD.ProviderPath)
Обратите внимание на собственный путь файловой системы текущего местоположения, $PWD.ProviderPath
, передается как справочный каталог.
Предостережение: если есть вероятность, что текущее местоположение находится на диске, отличном от диска файловой системы, используйте(Get-Location -PSProvider FileSystem).ProviderPath
для надежной ссылки на текущее расположение (каталог) файловой системы.
В Windows PowerShell вы можете использовать[IO.Path]::Combine()
, но учтите, что вам придется удалить ./
префикс вручную, если он вам не нужен в результирующем пути:
$dir = "./temp"
[IO.Path]::Combine($PWD.ProviderPath, $dir -replace '^.[\\/]')
[1] В то время как данный процесс обычно имеет только одно пространство выполнения PowerShell (сеанс), возможность сосуществования нескольких из них в процессе означает, что для всех из них концептуально невозможно синхронизировать свои отдельные рабочие каталоги с одним-единственным процессом - широкий рабочий каталог.NET. Более подробное объяснение можно найти в этой проблеме на GitHub.
Это больше похоже на .NET: почему объекты.NET в PowerShell не используют текущий каталог?
Powershell лечит .
как текущее местоположение. Итак, если вы это сделаетеGet-ChildItem -Path ".\"
, это вернет все в текущем каталоге. Чтобы получить относительный путь, вам нужно сделать что-то вроде этого:
Set-Location -Path "Path"
$path = Get-Item -Path "file" | Resolve-Path -Relative
$path
теперь будет относительный путь