Cómo obtener el directorio actual del cmdlet que se está ejecutando

202

Esta debería ser una tarea simple, pero he visto varios intentos sobre cómo obtener la ruta al directorio donde se encuentra el cmdlet ejecutado con éxito mixto. Por ejemplo, cuando ejecuto C:\temp\myscripts\mycmdlet.ps1que tiene un archivo de configuración en, C:\temp\myscripts\settings.xmlme gustaría poder almacenar C:\temp\myscriptsen una variable dentro mycmdlet.ps1.

Esta es una solución que funciona (aunque un poco engorrosa):

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

Otro sugirió esta solución que solo funciona en nuestro entorno de prueba:

$settingspath = '.\settings.xml'

Me gusta mucho este último enfoque y prefiero tener que analizar la ruta del archivo como parámetro cada vez, pero no puedo hacer que funcione en mi entorno de desarrollo. ¿Qué debo hacer? ¿Tiene algo que ver con cómo se configura PowerShell?

Stig Perez
fuente
11
Tenga en cuenta que el título ambiguo de esta pregunta ha dado como resultado que las respuestas a continuación resuelvan uno de dos problemas distintos, sin indicar explícitamente qué: (a) cómo hacer referencia a la ubicación actual (directorio) o (b) cómo hacer referencia a la ubicación del script en ejecución ( el directorio en el que se encuentra el script en ejecución, que puede o no ser el directorio actual).
mklement0

Respuestas:

144

La forma confiable de hacer esto es tal como la mostraste $MyInvocation.MyCommand.Path.

El uso de rutas relativas se basará en $ pwd, en PowerShell, el directorio actual para una aplicación o el directorio de trabajo actual para una API .NET.

PowerShell v3 + :

Utiliza la variable automática $PSScriptRoot.

JasonMArcher
fuente
66
¿Puede explicarme cómo encontró la propiedad RUTA? $ MyInvocation.MyCommand | gm no muestra dicha propiedad en la lista de miembros.
Vitaliy Markitanov
19
¿Por qué no usar $ PSScriptRoot? Parece más confiable
mBrice1024
@ user2326106 ¿Puede explicar la diferencia entre $PSScriptRooty $MyInvocation.MyCommand.Path?
duct_tape_coder
1
Esta respuesta es incompleta.
K - La toxicidad en SO está creciendo.
Esta respuesta es incompleta.
stackleit
263

Sí, eso debería funcionar. Pero si necesita ver el camino absoluto, esto es todo lo que necesita:

(Get-Item .).FullName
GuruKay
fuente
44
Gracias, este es un gran método para encontrar la ruta completa desde rutas relativas. Por ejemplo (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux
Gracias por esto. Otras respuestas no estaban funcionando para los scripts de Powershell compilados en EXEs.
Zach Alexander
10
Esto esta mal . Esto obtiene el directorio actual del proceso , que puede estar en cualquier lugar. Por ejemplo, si mi directorio actual de la línea de comando es C:\mydir, e invoco el comando C:\dir1\dir2\dir3\mycmdlet.ps1, entonces esto se resolverá C:\mydir, no C:\dir1\dir2\dir3. Invocar un nuevo ejecutable tiene el mismo problema ya que el directorio actual se hereda del proceso principal.
jpmc26
85

El método más fácil parece ser usar la siguiente variable predefinida:

 $PSScriptRoot

about_Automatic_Variablesy about_Scriptsambos dicen:

En PowerShell 2.0, esta variable solo es válida en los módulos de script (.psm1). A partir de PowerShell 3.0, es válido en todos los scripts.

Lo uso así:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Tony Sheen
fuente
12
Es específico de la versión. Esto requiere al menos Powershell 3.0.
Marvin Dickhaus
1
Esto es lo que necesitaba para hacer referencia a un archivo en la misma ubicación que el script, ¡gracias!
Adam Prescott
Esta es la mejor respuesta, ya que le brinda precisamente la ruta donde está presente su script PS, que es esencialmente la raíz de la ejecución de su script. No le importa cuál es su directorio de trabajo actual desde donde ha invocado sus scripts. +1.
RBT
@MarvinDickhaus Por eso es necesario usar "Set-StrictMode -Version 3.0" en la mayoría de sus guiones :) ¡Muchas gracias por los enlaces!
Alexander Shapkin
44

También puedes usar:

(Resolve-Path .\).Path

La parte entre paréntesis devuelve un PathInfoobjeto.

(Disponible desde PowerShell 2.0.)

Alex Angas
fuente
2
Esto esta mal . Esto obtiene el directorio actual del proceso , que puede estar en cualquier lugar. Por ejemplo, si mi directorio actual de la línea de comando es C:\mydir, e invoco el comando C:\dir1\dir2\dir3\mycmdlet.ps1, entonces esto se resolverá C:\mydir, no C:\dir1\dir2\dir3. Invocar un nuevo ejecutable tiene el mismo problema ya que el directorio actual se hereda del proceso principal.
jpmc26
3
¡Gracias! También entendí mal el título de esta pregunta, y esta respuesta fue exactamente lo que estaba buscando. Sin embargo ... No responde la pregunta.
Ryan The Leach
33

El camino es a menudo nulo. Esta función es más 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
fuente
1
¿Por qué -Scope 1? no -Scope 0
suiwenfeng
1
Get-Variable: el número de ámbito '1' excede el número de ámbitos activos.
suiwenfeng
2
Obtiene este error porque no tiene ámbito primario. El parámetro -Scope obtiene la variable en un alcance especificado. 1 en este caso es el alcance principal. Para obtener más información, consulte este artículo de Technet sobre Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem
27

Tratar :

(Get-Location).path

o:

($pwd).path
Rohin Sidharth
fuente
¡Siempre me olvido de $pwd/ $PWDcada vez que tomo un largo descanso de powershell! OMI mucho más útil ...
kayleeFrye_onDeck
17

Get-Location devolverá la ubicación actual:

$Currentlocation = Get-Location
Veera Induvasi
fuente
3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> esto le dará c: \ windows \ system32
nbi
11

Me gusta la solución de una línea :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
FrankyHollywood
fuente
Esta es la mejor solución hasta ahora.
Teoman shipahi
Si funciona para mi. Mi pwd es mi directorio de usuarios y estoy ejecutando el script en otro directorio (B) que da (B)
andrew pate
8

Prueba esto:

$WorkingDir = Convert-Path .
Séptuple Exurbanita
fuente
4

En Powershell 3 y superior simplemente puede usar

$PSScriptRoot

BlackSpy
fuente
1

Se podría pensar que usar '. \' Como ruta significa que es la ruta de invocación. Pero no todo el tiempo. Ejemplo, si lo usa dentro de un trabajo ScriptBlock. En cuyo caso, podría apuntar a% profile% \ Documents.

syanzy
fuente
1

Esta función establecerá la ubicación del indicador en la ruta del script, tratando con las diferentes formas de obtener scriptpath entre vscode, psise y 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
fuente
1

La mayoría de las respuestas no funcionan al depurar en los siguientes IDE:

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

Porque en esos el $PSScriptRootestá vacío y Resolve-Path .\(y similares) dará como resultado rutas incorrectas.

La respuesta de Freakydinde es la única que resuelve esas situaciones, por lo que voté a favor, pero no creo Set-Locationque la respuesta sea realmente lo que se desea. Así que arreglé eso e hice el código un poco más claro:

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

Para lo que vale, para ser una solución de una sola línea, la siguiente es una solución que funciona para mí.

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

El 1 al final es ignorar el /.

Gracias a las publicaciones anteriores usando el cmdlet Get-Location .

Ak777
fuente
-1

Para ampliar la respuesta de @Cradle: también puede escribir una función multipropósito que le dará el mismo resultado según la pregunta del 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
fuente
-1

Si solo necesita el nombre del directorio actual, puede hacer algo como esto:

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

Suponiendo que está trabajando desde C: \ Temp \ Location \ MyWorkingDirectory>

Salida

MyWorkingDirectory

SolThoth
fuente
-1

Tuve problemas similares y me causó muchos problemas ya que estoy haciendo programas escritos en PowerShell (aplicaciones GUI de usuario final) y tengo muchos archivos y recursos que necesito cargar desde el disco. Desde mi experiencia, usando . Después de eso, PowerShell cambia el directorio a cualquier directorio desde el que lo invocó, o al directorio donde se encuentra el script que está ejecutando antes de presentarle el indicador de PowerShell o ejecutar el script. Pero eso sucede después de que la aplicación PowerShell se inicia originalmente dentro de su directorio de usuario doméstico.. para representar el directorio actual no es confiable. Debe representar el directorio de trabajo actual, pero a menudo no lo hace. Parece que PowerShell guarda la ubicación desde la cual se ha invocado a PowerShell .. Para ser más precisos, cuando PowerShell se inicia por primera vez, se inicia, de manera predeterminada, dentro de su directorio de usuario doméstico. Ese suele ser el directorio de su cuenta de usuario, algo así comoC:\USERS\YOUR USER NAME

Y .representa ese directorio inicial dentro del cual se inició PowerShell. Por lo tanto, .solo representa el directorio actual en caso de que haya invocado PowerShell desde el directorio deseado. Si luego cambia el directorio en el código de PowerShell, el cambio parece no reflejarse .en el interior en todos los casos. En algunos casos .representa el directorio de trabajo actual, y en otros directorio desde el que se ha invocado PowerShell (en sí mismo, no el script), lo que puede conducir a resultados inconsistentes. Por esta razón, uso el script de invocador. Script de PowerShell con un solo comando dentro: representa el directorio actual. Pero solo funciona si no cambia el directorio más adelante en el código de PowerShell. En el caso de un script, utilizo un script de invocador que es similar al último que mencioné, excepto que contiene una opción de archivo: POWERSHELL . Eso garantizará que se invoque PowerShell desde el directorio deseado y, por lo tanto,.POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1 . Eso asegura que PowerShell se inicie dentro del directorio de trabajo actual.

Simplemente haciendo clic en la secuencia de comandos invoca PowerShell desde el directorio de usuario de su casa sin importar dónde se encuentre la secuencia de comandos. Resulta que el directorio de trabajo actual es el directorio donde se encuentra el script, pero el directorio de invocación de PowerShell esC:\USERS\YOUR USER NAME , y con la .devolución de uno de estos dos directorios dependiendo de la situación, lo que es ridículo.

Pero para evitar todo este alboroto y usar el script de invocador, simplemente puede usar cualquiera $PWDo en $PSSCRIPTROOTlugar de .representar el directorio actual dependiendo del clima en el que desee representar el directorio de trabajo actual o el directorio desde el que se invocó el script. Y si, por alguna razón, desea recuperar otro de dos directorios que. regresa, puede usarlo $HOME.

Personalmente, solo tengo un script de invocador dentro del directorio raíz de mis aplicaciones que desarrollo con PowerShell que invoca el script de mi aplicación principal, y simplemente recuerdo que nunca cambie el directorio de trabajo actual dentro del código fuente de mi aplicación, por lo que nunca tengo que preocuparme por esto, y puedo usar .para representar el directorio actual y para admitir el direccionamiento de archivos relativo en mis aplicaciones sin ningún problema. Esto debería funcionar en las versiones más nuevas de PowerShell (más nuevas que la versión 2).

SYOB SYOT
fuente