Como obter o diretório atual do cmdlet que está sendo executado

202

Essa deve ser uma tarefa simples, mas já vi várias tentativas de como obter o caminho para o diretório em que o cmdlet executado está localizado com sucesso misto. Por exemplo, quando eu executo C:\temp\myscripts\mycmdlet.ps1um arquivo de configurações C:\temp\myscripts\settings.xml, gostaria de poder armazenar C:\temp\myscriptsuma variável dentro dele mycmdlet.ps1.

Esta é uma solução que funciona (embora um pouco complicada):

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

Outro sugeriu esta solução, que funciona apenas em nosso ambiente de teste:

$settingspath = '.\settings.xml'

Eu gosto muito da última abordagem e prefiro ter que analisar o caminho do arquivo como parâmetro a cada vez, mas não consigo fazê-lo funcionar no meu ambiente de desenvolvimento. O que devo fazer? Tem algo a ver com a forma como o PowerShell está configurado?

Stig Perez
fonte
11
Observe que o título ambíguo desta pergunta resultou nas respostas abaixo, resolvendo um dos dois problemas distintos, sem declarar explicitamente quais: (a) como referenciar o local atual (diretório) ou (b) como referenciar o local do script em execução ( o diretório em que o script em execução está localizado, que pode ou não ser o diretório atual).
usar o seguinte comando

Respostas:

143

A maneira confiável de fazer isso é exatamente como você mostrou $MyInvocation.MyCommand.Path.

O uso de caminhos relativos será baseado em $ pwd, no PowerShell, no diretório atual de um aplicativo ou no diretório de trabalho atual de uma API .NET.

PowerShell v3 + :

Use a variável automática $PSScriptRoot.

JasonMArcher
fonte
6
Você pode me explicar como encontrou a propriedade PATH? $ MyInvocation.MyCommand | gm não mostra essa propriedade na lista de membros.
Vitaliy Markitanov 11/12/16
19
por que não usar apenas $ PSScriptRoot? Parece mais confiável
mBrice1024
@ user2326106 Você pode explicar a diferença entre $PSScriptRoote $MyInvocation.MyCommand.Path?
duct_tape_coder
1
Esta resposta está incompleta.
K - A toxicidade no SO está crescendo.
Esta resposta está incompleta.
stackleit
263

Sim, isso deve funcionar. Mas se você precisa ver o caminho absoluto, é tudo o que precisa:

(Get-Item .).FullName
GuruKay
fonte
4
Obrigado, este é um ótimo método para encontrar o caminho completo a partir de caminhos relativos. Por exemplo, (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux
Obrigado por isso. Outras respostas não estavam funcionando para scripts do PowerShell compilados para EXEs.
Zach Alexander #
10
Isto está errado . Isso obtém o diretório atual do processo , que pode estar em qualquer lugar. Por exemplo, se meu diretório atual da linha de comando for C:\mydire eu chamar o comando C:\dir1\dir2\dir3\mycmdlet.ps1, isso resolverá C:\mydirnão C:\dir1\dir2\dir3. A chamada de um novo executável tem o mesmo problema, pois o diretório atual é herdado do processo pai.
Jpmc26 13/04/2018
85

O método mais fácil parece ser usar a seguinte variável predefinida:

 $PSScriptRoot

about_Automatic_Variablese about_Scriptsambos afirmam:

No PowerShell 2.0, essa variável é válida apenas nos módulos de script (.psm1). A partir do PowerShell 3.0, é válido em todos os scripts.

Eu uso assim:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Tony Sheen
fonte
12
É específico da versão. Isso requer pelo menos o Powershell 3.0.
Marvin Dickhaus
1
Era isso que eu precisava para referenciar um arquivo no mesmo local que o script - obrigado!
Adam Prescott
Esta é a melhor resposta, pois fornece exatamente o caminho em que o script PS está presente, que é essencialmente a raiz da execução do script. Não importa qual é o seu diretório de trabalho atual de onde você chamou seus scripts. +1.
RBT
@MarvinDickhaus É por isso que é necessário usar "Set-StrictMode -Version 3.0" na maioria dos seus scripts :) Muito obrigado pelos links!
Alexander Shapkin
44

Você também pode usar:

(Resolve-Path .\).Path

A peça entre parênteses retorna um PathInfo objeto.

(Disponível desde o PowerShell 2.0.)

Alex Angas
fonte
2
Isto está errado . Isso obtém o diretório atual do processo , que pode estar em qualquer lugar. Por exemplo, se meu diretório atual da linha de comando for C:\mydire eu chamar o comando C:\dir1\dir2\dir3\mycmdlet.ps1, isso resolverá C:\mydirnão C:\dir1\dir2\dir3. A chamada de um novo executável tem o mesmo problema, pois o diretório atual é herdado do processo pai.
jpmc26
3
Obrigado! Eu também não entendi o título desta pergunta, e essa resposta era exatamente o que eu estava procurando. No entanto ... Não responde à pergunta.
Ryan The Leach
33

O caminho geralmente é nulo. Esta função é mais segura.

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("\"));
    }
}
Christian Flem
fonte
1
por que -Scope 1? Não -Scope 0
suiwenfeng
1
Get-Variable: o número do escopo '1' excede o número de escopos ativos.
suiwenfeng
2
Você está recebendo esse erro porque não possui escopo pai. -Scope parâmetro obtém a variável em um escopo especificado. 1 neste caso é o escopo pai. Para obter mais informações, consulte este artigo técnico sobre Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem
27

Experimentar :

(Get-Location).path

ou:

($pwd).path
Rohin Sidharth
fonte
Continuo esquecendo $pwd/ $PWDsempre que faço uma longa pausa no PowerShell! Portanto, muito mais útil IMO ...
kayleeFrye_onDeck
17

Get-Location retornará o local atual:

$Currentlocation = Get-Location
Veera Induvasi
fonte
3
PS C: \ Windows \ system32> C: \ PowerShell \ checkfile.ps1 -> isso vai dar c: \ windows \ system32
nbi
11

Eu gosto da solução de uma linha :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
FrankyHollywood
fonte
Esta é a melhor solução até agora.
Teoman shipahi
Sim funciona para mim. Meu pwd é o meu diretório do usuário e eu estou correndo o script em outro diretório (B) dá (B)
andrew pate
8

Tente o seguinte:

$WorkingDir = Convert-Path .
Exurbanita de Sete Vezes
fonte
4

No Powershell 3 e acima, você pode simplesmente usar

$PSScriptRoot

BlackSpy
fonte
1

Você pensaria que usar '. \' Como o caminho significa que é o caminho da invocação. Mas não o tempo todo. Exemplo, se você o usar dentro de um trabalho ScriptBlock. Nesse caso, pode apontar para% profile% \ Documents.

syanzy
fonte
1

Essa função definirá o local do prompt para o caminho do script, lidando com a maneira diferente de obter o caminho do script entre vscode, psise e 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
fonte
0

Pelo que vale a pena, por ser uma solução de linha única, a seguir é uma solução funcional para mim.

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

O 1 no final é ignorar o / .

Graças às postagens acima, usando o cmdlet Get-Location .

Ak777
fonte
0

A maioria das respostas não funciona ao depurar nos seguintes IDEs:

  • PS-ISE (PowerShell ISE)
  • Código VS (código do Visual Studio)

Porque nesses $PSScriptRootestá vazio e Resolve-Path .\(e similares) resultará em caminhos incorretos.

A resposta de Freakydinde é a única que resolve essas situações, por isso votei positivamente , mas não acho Set-Locationque a resposta seja realmente o que é desejado. Então eu corrigi isso e deixei o código um pouco mais claro:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
Mariano Desanze
fonte
-1

Para expandir a resposta do @Cradle: você também pode escrever uma função multiuso que obterá o mesmo resultado de acordo com a pergunta do OP:

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
fonte
-1

Se você apenas precisar do nome do diretório atual, poderá fazer algo assim:

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

Supondo que você esteja trabalhando em C: \ Temp \ Location \ MyWorkingDirectory>

Resultado

MyWorkingDirectory

SolThoth
fonte
-1

Eu tive problemas semelhantes e isso me causou muitos problemas, pois estou criando programas escritos no PowerShell (aplicativos de interface gráfica do usuário final) e tenho muitos arquivos e recursos que preciso carregar do disco. Da minha experiência, usar .para representar o diretório atual não é confiável. Ele deve representar o diretório de trabalho atual, mas geralmente não. Parece que o PowerShell salva o local do qual o PowerShell foi chamado dentro .. Para ser mais preciso, quando o PowerShell é iniciado pela primeira vez, ele inicia, por padrão, dentro do diretório de usuário doméstico. Esse geralmente é o diretório da sua conta de usuário, algo comoC:\USERS\YOUR USER NAME. Depois disso, o PowerShell altera o diretório para o diretório do qual você o invocou ou para o diretório em que o script que você está executando está localizado antes de apresentar o prompt do PowerShell ou de executar o script. Mas isso acontece depois que o próprio aplicativo PowerShell é iniciado originalmente dentro do diretório de usuário doméstico.

E .representa o diretório inicial no qual o PowerShell foi iniciado. Portanto, .apenas representa o diretório atual, caso você tenha chamado o PowerShell a partir do diretório desejado. Se você alterar posteriormente o diretório no código do PowerShell, as alterações não aparecerão .em todos os casos. Em alguns casos, .representa o diretório de trabalho atual e, em outros, do qual o PowerShell (não o script) foi invocado, o que pode levar a resultados inconsistentes. Por esse motivo, eu uso o script invoker. Script do PowerShell com interior comando único: POWERSHELL. Isso garantirá que o PowerShell seja chamado do diretório desejado e, assim, tornará. representa o diretório atual. Mas isso só funciona se você não alterar o diretório posteriormente no código do PowerShell. No caso de um script, eu uso o script invoker que é semelhante ao último que mencionei, exceto que contém uma opção de arquivo: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Isso garante que o PowerShell seja iniciado dentro do diretório de trabalho atual.

Simplesmente clicar no script chama o PowerShell do diretório de usuário doméstico, não importa onde o script esteja localizado. Isso resulta no diretório de trabalho atual sendo o diretório em que o script está localizado, mas o diretório de chamada do PowerShell sendo C:\USERS\YOUR USER NAMEe .retornando um desses dois diretórios, dependendo da situação, o que é ridículo.

Mas, para evitar toda essa confusão e usar o script invoker, você pode simplesmente usar um $PWDou ao $PSSCRIPTROOTinvés de .representar o diretório atual, dependendo do clima em que deseja representar o diretório de trabalho atual ou o diretório do qual o script foi invocado. E se você, por algum motivo, quiser recuperar outros dois diretórios .retornados, poderá usá-lo $HOME.

Pessoalmente, tenho apenas um script de invocação no diretório raiz dos meus aplicativos que desenvolvo com o PowerShell, que invoca meu script de aplicativo principal, e lembre-se de nunca alterar o diretório de trabalho atual dentro do código-fonte do meu aplicativo, para que nunca precise me preocupar com isso, e posso usar .para representar o diretório atual e oferecer suporte ao endereçamento relativo de arquivos em meus aplicativos sem problemas. Isso deve funcionar em versões mais recentes do PowerShell (mais recentes que a versão 2).

SYOB SYOT
fonte