Как получить текущий каталог выполняемого командлета

202

Это должно быть простой задачей, но я видел несколько попыток получить путь к каталогу, в котором исполняемый командлет находится с переменным успехом. Например, когда я выполняю C:\temp\myscripts\mycmdlet.ps1файл с настройками, C:\temp\myscripts\settings.xmlя хотел бы иметь возможность хранить C:\temp\myscriptsв переменной внутри mycmdlet.ps1.

Это одно решение, которое работает (хотя и немного громоздко):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

Другой предложил это решение, которое работает только в нашей тестовой среде:

$settingspath = '.\settings.xml'

Мне очень нравится последний подход, и я предпочитаю каждый раз анализировать путь к файлу как параметр, но я не могу заставить его работать в моей среде разработки. Что мне делать? Это как-то связано с настройкой PowerShell?

Стиг Перес
источник
11
Обратите внимание, что неоднозначное название этого вопроса привело к тому, что приведенные ниже ответы решают одну из двух различных проблем, не указывая явно: а) как ссылаться на текущее местоположение (каталог) или (б) как ссылаться на местоположение выполняемого сценария ( каталог, в котором находится запущенный скрипт, который может быть или не быть текущим каталогом).
mklement0

Ответы:

144

Надежный способ сделать это, как вы показали $MyInvocation.MyCommand.Path.

Использование относительных путей будет основано на $ pwd в PowerShell, текущем каталоге для приложения или текущем рабочем каталоге для .NET API.

PowerShell v3 + :

Используйте автоматическую переменную $PSScriptRoot.

JasonMArcher
источник
6
Не могли бы вы объяснить, как вы нашли собственность PATH? $ MyInvocation.MyCommand | gm не показывает такое свойство в списке участников.
Виталий Маркитанов
19
почему бы просто не использовать $ PSScriptRoot? Кажется более надежным
mBrice1024
@ user2326106 Можете ли вы объяснить разницу между $PSScriptRootи $MyInvocation.MyCommand.Path?
duct_tape_coder
1
Этот ответ неполон.
К - Токсичность в СО растет.
Этот ответ неполон.
стек
263

Да, это должно работать. Но если вам нужно увидеть абсолютный путь, это все, что вам нужно:

(Get-Item .).FullName
GuruKay
источник
4
Спасибо, это отличный способ найти полный путь из относительных путей. Например (Get-Item -Path $ myRelativePath -Verbose) .ullName
dlux,
Спасибо тебе за это. Другие ответы не работали для скриптов Powershell, скомпилированных в EXE-файлы.
Зак Александр
10
Это неправильно . Это получает текущий каталог процесса , который может быть где угодно. Например, если текущим каталогом моей командной строки является C:\mydir, и я вызываю команду C:\dir1\dir2\dir3\mycmdlet.ps1, то это разрешит C:\mydir, а не C:\dir1\dir2\dir3. Вызов нового исполняемого файла имеет ту же проблему, поскольку текущий каталог наследуется от родительского процесса.
jpmc26
85

Кажется, самый простой способ - использовать следующую предопределенную переменную:

 $PSScriptRoot

about_Automatic_Variablesи about_Scriptsоба заявляют:

В PowerShell 2.0 эта переменная действительна только в скриптовых модулях (.psm1). Начиная с PowerShell 3.0, он действителен во всех сценариях.

Я использую это так:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Тони Шин
источник
12
Это зависит от версии. Для этого требуется как минимум Powershell 3.0.
Марвин Дикхаус
1
Это то, что мне нужно для ссылки на файл в том же месте, что и скрипт - спасибо!
Адам Прескотт
Это лучший ответ, так как он дает вам точный путь, где присутствует ваш скрипт PS, который по сути является корнем для выполнения вашего скрипта. Неважно, каков ваш нынешний рабочий каталог, откуда вы вызывали свои скрипты. +1.
RBT
@MarvinDickhaus Вот почему в большинстве ваших сценариев необходимо использовать «Set-StrictMode -Version 3.0» :) Большое спасибо за ссылки!
Александр Шапкин
44

Вы также можете использовать:

(Resolve-Path .\).Path

Часть в скобках возвращает PathInfoобъект.

(Доступно с PowerShell 2.0.)

Алекс Ангас
источник
2
Это неправильно . Это получает текущий каталог процесса , который может быть где угодно. Например, если текущим каталогом моей командной строки является C:\mydir, и я вызываю команду C:\dir1\dir2\dir3\mycmdlet.ps1, то это разрешит C:\mydir, а не C:\dir1\dir2\dir3. Вызов нового исполняемого файла имеет ту же проблему, поскольку текущий каталог наследуется от родительского процесса.
jpmc26
3
Спасибо! Я также неправильно понял название этого вопроса, и этот ответ был именно тем, что я искал. Однако ... Это не отвечает на вопрос.
Райан Лич
33

Путь часто нулевой. Эта функция безопаснее.

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value;
    if($Invocation.PSScriptRoot)
    {
        $Invocation.PSScriptRoot;
    }
    Elseif($Invocation.MyCommand.Path)
    {
        Split-Path $Invocation.MyCommand.Path
    }
    else
    {
        $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
    }
}
Кристиан Флем
источник
1
почему - сфера 1? не сфера 0
suiwenfeng
1
Get-Variable: номер области «1» превышает количество активных областей.
suiwenfeng
2
Вы получаете эту ошибку, потому что у вас нет родительской области видимости. -Scope параметр получает переменную в указанной области. 1 в этом случае является родительской областью. Для получения дополнительной информации см. Эту статью Technet о Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Кристиан
27

Пытаться :

(Get-Location).path

или:

($pwd).path
Рохин Сидхарт
источник
Я постоянно забываю о $pwd/ $PWDвсякий раз, когда делаю длительный перерыв от powershell! Намного полезнее ИМО ...
kayleeFrye_onDeck
17

Get-Location вернет текущее местоположение:

$Currentlocation = Get-Location
Veera Induvasi
источник
3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> это даст c: \ windows \ system32
nbi
11

Мне нравится однострочное решение :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
FrankyHollywood
источник
Это лучшее решение на сегодняшний день.
Теоман Шипахи
Да, у меня работает. Мой pwd - это мой каталог пользователя, и я запускаю скрипт в другом каталоге (B), который он выдает (B)
andrew pate
8

Попробуй это:

$WorkingDir = Convert-Path .
Семикратный Экзурбанит
источник
4

В Powershell 3 и выше вы можете просто использовать

$PSScriptRoot

BlackSpy
источник
1

Можно подумать, что использование «. \» В качестве пути означает, что это путь вызова. Но не все время. Пример, если вы используете его внутри задания ScriptBlock. В этом случае это может указывать на% profile% \ Documents.

syanzy
источник
1

эта функция установит в строке приглашения путь к сценарию, имея дело с разным способом получения пути к сценарию между vscode, psise и pwd:

function Set-CurrentLocation
{
    $currentPath = $PSScriptRoot                                                                                                     # AzureDevOps, Powershell
    if (!$currentPath) { $currentPath = Split-Path $pseditor.GetEditorContext().CurrentFile.Path -ErrorAction SilentlyContinue }     # VSCode
    if (!$currentPath) { $currentPath = Split-Path $psISE.CurrentFile.FullPath -ErrorAction SilentlyContinue }                       # PsISE

    if ($currentPath) { Set-Location $currentPath }
}
freakydinde
источник
1

Большинство ответов не работают при отладке в следующих средах разработки:

  • PS-ISE (PowerShell ISE)
  • Код VS (Код Visual Studio)

Потому что в тех $PSScriptRootпусто иResolve-Path .\ (и аналогичных) приведет к неправильным путям.

Ответ Freakydinde - единственный, который разрешает эти ситуации, поэтому я проголосовал за это, но я не думаю, что Set-Locationв этом ответе действительно то , что нужно . Поэтому я исправил это и сделал код немного более понятным:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
Мариано Дезанце
источник
0

То, что стоит, чтобы быть однострочным решением, ниже является рабочим решением для меня.

$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)

1 в конце должен игнорировать / .

Благодаря приведенным выше сообщениям с помощью командлета Get-Location .

Ak777
источник
-1

Чтобы расширить ответ @Cradle: вы также можете написать многоцелевую функцию, которая даст вам тот же результат в ответ на вопрос ОП:

Function Get-AbsolutePath {

    [CmdletBinding()]
    Param(
        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true
        )]
        [String]$relativePath=".\"
    )

    if (Test-Path -Path $relativePath) {
        return (Get-Item -Path $relativePath).FullName -replace "\\$", ""
    } else {
        Write-Error -Message "'$relativePath' is not a valid path" -ErrorId 1 -ErrorAction Stop
    }

}
Erutan409
источник
-1

Если вам просто нужно имя текущего каталога, вы можете сделать что-то вроде этого:

((Get-Location) | Get-Item).Name

Предполагая, что вы работаете с C: \ Temp \ Location \ MyWorkingDirectory>

Вывод

MyWorkingDirectory

SolThoth
источник
-1

У меня были подобные проблемы, и это доставило мне много хлопот, так как я делаю программы, написанные на PowerShell (приложения с полным пользовательским интерфейсом), и у меня много файлов и ресурсов, которые мне нужно загрузить с диска. Из моего опыта использования. для представления текущего каталога ненадежно. Он должен представлять текущий рабочий каталог, но часто это не так. Похоже, что PowerShell сохраняет расположение, из которого был вызван PowerShell .. Точнее говоря, при первом запуске PowerShell он запускается по умолчанию внутри вашего домашнего каталога пользователя. Обычно это каталог вашей учетной записи, что-то вродеC:\USERS\YOUR USER NAME, После этого PowerShell меняет каталог на каталог, из которого вы его вызвали, или на каталог, в котором находится выполняемый скрипт, перед тем, как представить вам приглашение PowerShell или запустить скрипт. Но это происходит после того, как само приложение PowerShell изначально запускается в вашем домашнем пользовательском каталоге.

И .представляет тот начальный каталог, внутри которого запускается PowerShell. Таким образом, .представляет текущий каталог только в том случае, если вы вызвали PowerShell из требуемого каталога. Если вы позже измените каталог в коде PowerShell, изменение не будет отражаться внутри .каждого случая. В некоторых случаях .представляет текущий рабочий каталог, а в других каталог, из которого был вызван PowerShell (сам по себе, а не сценарий), что может привести к противоречивым результатам. По этой причине я использую скрипт invoker. PowerShell скрипт с одной командой внутри: POWERSHELL. Это гарантирует, что PowerShell вызывается из требуемого каталога и, таким образом,.представлять текущий каталог. Но это работает только в том случае, если вы не измените каталог позже в коде PowerShell. В случае сценария, я использую INVOKER скрипт , который похож на последнего я уже говорил, за исключением того, что содержит опцию Файл: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Это обеспечивает запуск PowerShell внутри текущего рабочего каталога.

Простое нажатие на скрипт вызывает PowerShell из вашего домашнего пользовательского каталога независимо от того, где находится скрипт. Это приводит к тому, что текущим рабочим каталогом является каталог, в котором находится сценарий, а каталогом вызова PowerShell - C:\USERS\YOUR USER NAMEи с .возвратом одного из этих двух каталогов в зависимости от ситуации, что смешно.

Но чтобы избежать всей этой суеты и использования сценария invoker, вы можете просто использовать один $PWDили $PSSCRIPTROOTвместо .представления текущего каталога в зависимости от того, какую погоду вы хотите представить текущий рабочий каталог или каталог, из которого был вызван сценарий. И если вы по какой-то причине хотите получить другие из двух каталогов, которые .возвращаются, вы можете использовать $HOME.

Лично у меня просто есть скрипт invoker внутри корневого каталога моих приложений, который я разрабатываю с помощью PowerShell, который вызывает мой основной скрипт приложения, и просто не забываю никогда не менять текущий рабочий каталог внутри моего исходного кода моего приложения, поэтому мне никогда не придется беспокоиться об этом, и я могу использовать .для представления текущего каталога и для поддержки относительной адресации файлов в моих приложениях без каких-либо проблем. Это должно работать в более новых версиях PowerShell (новее версии 2).

SYOB SYOT
источник