¿Cómo puedo obtener el archivo de ejecución actual de PowerShell?

92

Nota: PowerShell 1.0
Me gustaría obtener el nombre de archivo de PowerShell en ejecución actual. Es decir, si comienzo mi sesión así:

powershell.exe .\myfile.ps1

Me gustaría obtener la cadena ". \ Myfile.ps1" (o algo así). EDITAR : "myfile.ps1" es preferible.
¿Algunas ideas?

Ron Klein
fuente
Gracias, las respuestas actuales son casi las mismas, pero solo necesito el nombre del archivo (y no la ruta completa), por lo que la respuesta aceptada es la de @ Keith. Sin embargo, +1 a ambas respuestas. Ahora sé lo de $ MyInvocation :-)
Ron Klein
¿Qué tal obtener el script principal de un script incluido?
Florin Sabau

Respuestas:

67

He intentado resumir las diversas respuestas aquí, actualizado para PowerShell 5:

  • Si solo usa PowerShell 3 o superior, use $PSCommandPath

  • Si desea compatibilidad con versiones anteriores, inserte la cuña:

    if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath; }

    Esto agrega $PSCommandPathsi aún no existe.

    El código de corrección se puede ejecutar en cualquier lugar (nivel superior o dentro de una función), aunque la $PSCommandPathvariable está sujeta a las reglas de alcance normales (por ejemplo, si coloca la corrección en una función, la variable tiene el alcance únicamente para esa función).

Detalles

Hay 4 métodos diferentes utilizados en varias respuestas, así que escribí este script para demostrar cada uno (más $PSCommandPath):

function PSCommandPath() { return $PSCommandPath; }
function ScriptName() { return $MyInvocation.ScriptName; }
function MyCommandName() { return $MyInvocation.MyCommand.Name; }
function MyCommandDefinition() {
    # Begin of MyCommandDefinition()
    # Note: ouput of this script shows the contents of this function, not the execution result
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return $MyInvocation.PSCommandPath; }

Write-Host "";
Write-Host "PSVersion: $($PSVersionTable.PSVersion)";
Write-Host "";
Write-Host "`$PSCommandPath:";
Write-Host " *   Direct: $PSCommandPath";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.ScriptName:";
Write-Host " *   Direct: $($MyInvocation.ScriptName)";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Name:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Name)";
Write-Host " * Function: $(MyCommandName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Definition:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Definition)";
Write-Host " * Function: $(MyCommandDefinition)";
Write-Host "";
Write-Host "`$MyInvocation.PSCommandPath:";
Write-Host " *   Direct: $($MyInvocation.PSCommandPath)";
Write-Host " * Function: $(MyInvocationPSCommandPath)";
Write-Host "";

Salida:

PS C:\> .\Test\test.ps1

PSVersion: 5.1.19035.1

$PSCommandPath:
 *   Direct: C:\Test\test.ps1
 * Function: C:\Test\test.ps1

$MyInvocation.ScriptName:
 *   Direct:
 * Function: C:\Test\test.ps1

$MyInvocation.MyCommand.Name:
 *   Direct: test.ps1
 * Function: MyCommandName

$MyInvocation.MyCommand.Definition:
 *   Direct: C:\Test\test.ps1
 * Function:
    # Begin of MyCommandDefinition()
    # Note this is the contents of the MyCommandDefinition() function, not the execution results
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()


$MyInvocation.PSCommandPath:
 *   Direct:
 * Function: C:\Test\test.ps1

Notas:

  • Ejecutado de C:\, pero el script real es C:\Test\test.ps1.
  • Ningún método le dice la ruta de invocación pasada ( .\Test\test.ps1)
  • $PSCommandPath es la única forma confiable, pero se introdujo en PowerShell 3
  • Para las versiones anteriores a la 3, ningún método funciona tanto dentro como fuera de una función
Gregmac
fuente
7
Para cualquiera que lea hoy (2017), ¡debería leer ESTA publicación como la respuesta correcta! +1
Collin Chaffin
2
@CollinChaffin: acordado y ahora (2017) el menos compatible actualmente es Windows 7, por lo que no hay razón para no usarlo $PSCommandPathsi no se requiere el legado (WindowsXP).
Tukan
El primer ejemplo de código es defectuoso ya que contiene dos definiciones de la misma función ( function PSCommandPath) y una referencia a la función incorrecta ( Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";- ¿o estoy pasando por alto algo obvio?
Mike L'Angelo
@ MikeL'Angelo ¡Tienes razón! Pasó desapercibido durante 3 años. Arreglado, gracias. Sin embargo, el resultado y la conclusión son los mismos.
Gregmac
81

Si bien la respuesta actual es correcta en la mayoría de los casos, hay ciertas situaciones en las que no le dará la respuesta correcta. Si usa dentro de las funciones de su script, entonces:

$MyInvocation.MyCommand.Name 

Devuelve el nombre de la función en lugar del nombre del script.

function test {
    $MyInvocation.MyCommand.Name
}

Le dará " prueba " sin importar cómo se llame su script. El comando correcto para obtener el nombre del script es siempre

$MyInvocation.ScriptName

esto devuelve la ruta completa del script que está ejecutando. Si solo necesita el nombre del archivo de secuencia de comandos, este código debería ayudarlo:

split-path $MyInvocation.PSCommandPath -Leaf
Lukas Kucera
fuente
6
Tenga en cuenta que en el nivel superior, Scriptname no está definido con posh v4. Me gusta usar en el nivel superior, $ MyInvocation.MyCommand.Definition para la ruta completa o el nombre según las otras respuestas.
AnneTheAgile
30
$MyInvocation.ScriptNamedevuelve una cadena vacía para mí, PS v3.0.
JohnC
4
@JohnC $MyInvocation.ScriptNamesolo funciona desde dentro de una función. Vea mi respuesta a continuación .
gregmac
72

Si solo desea el nombre del archivo (no la ruta completa), use esto:

$ScriptName = $MyInvocation.MyCommand.Name
Keith Hill
fuente
32

Prueba lo siguiente

$path =  $MyInvocation.MyCommand.Definition 

Es posible que esto no le proporcione la ruta real escrita, pero le dará una ruta válida al archivo.

JaredPar
fuente
1
@Hamish la pregunta dice específicamente si se invoca desde un archivo.
JaredPar
FYI: Esto le da la ruta completa y el nombre del archivo (Powershell 2.0)
Ralph Willgoss
Estaba buscando exactamente este comando. ¡Gracias, JaredPar! :)
sqlfool
¿Usar Split-Path para obtener el directorio? $path = Split-Path $MyInvocation.MyCommand.Definition -Parent
Underverse
7

Si está buscando el directorio actual en el que se está ejecutando el script, puede probar este:

$fullPathIncFileName = $MyInvocation.MyCommand.Definition
$currentScriptName = $MyInvocation.MyCommand.Name
$currentExecutingPath = $fullPathIncFileName.Replace($currentScriptName, "")

Write-Host $currentExecutingPath
Ryk
fuente
1
Eso no funcionaría correctamente C:\ilike.ps123\ke.ps1, ¿verdad?
Fridojet
@fridojet - No estoy seguro, no cerca de un terminal PS para probarlo. ¿Por qué no lo intentas y ves?
Ryk
No, solo una pregunta retórica ;-) - Sería simplemente lógico porque el Replace()método reemplaza cada aparición de la aguja (no solo la última aparición) y también lo probé. Sin embargo, es una buena idea hacer algo como restar cadenas.
Fridojet
... ¿Qué pasa con String.TrimEnd()( $currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName))? - Funciona correctamente: "Ich bin Hamster".TrimEnd("ster")vuelve Ich bin Hamy "Ich bin Hamsterchen".TrimEnd("ster")vuelve Ich bin Hamsterchen(en lugar de Ich bin Hamchen) - ¡Bien!
Fridojet
$currentScriptPath = $MyInvocation.MyCommand.Definition; $currentScriptName = $MyInvocation.MyCommand.Name; $currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));
YP
7

cuidado: a diferencia de las variables automáticas $PSScriptRooty $PSCommandPath, las propiedades PSScriptRooty PSCommandPathdel$MyInvocation variable automática contienen información sobre el invocador o el script de llamada, no sobre el script actual.

p.ej

PS C:\Users\S_ms\OneDrive\Documents> C:\Users\SP_ms\OneDrive\Documents\DPM ...
=!C:\Users\S_ms\OneDrive\Documents\DPM.ps1

... donde DPM.ps1contiene

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)
MWR
fuente
4

Yo diría que hay un método mejor, estableciendo el alcance de la variable $ MyInvocation.MyCommand.Path:

ex> $ script : MyInvocation.MyCommand.Name

Este método funciona en todas las circunstancias de invocación:

EJ .: Somescript.ps1

function printme () {
    "In function:"
    ( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
    ( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
    ( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
}
"Main:"
( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
" "
printme
exit

SALIDA:

PS> powershell C:\temp\test.ps1
Main:
MyInvocation.ScriptName:
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: test.ps1

In function:
MyInvocation.ScriptName: C:\temp\test.ps1
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: printme

Observe cómo la respuesta aceptada anterior NO devuelve un valor cuando se llama desde Main. Además, tenga en cuenta que la respuesta aceptada anterior devuelve la ruta completa cuando la pregunta solicitó solo el nombre del script. La variable de ámbito funciona en todos los lugares.

Además, si quisiera la ruta completa, simplemente llamaría:

$script:MyInvocation.MyCommand.Path
Daddio
fuente
3

Como se señaló en respuestas anteriores, el uso de "$ MyInvocation" está sujeto a problemas de alcance y no necesariamente proporciona datos consistentes (valor de retorno frente a valor de acceso directo). Descubrí que el método "más limpio" (más consistente) para obtener información del script, como la ruta del script, el nombre, los parámetros, la línea de comando, etc., independientemente del alcance (en las llamadas de función principal o subsiguientes / anidadas) es usar "Get- Variable "en" MyInvocation "...

# Get the MyInvocation variable at script level
# Can be done anywhere within a script
$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

# Get the full path to the script
$ScriptPath = $ScriptInvocation.MyCommand.Path

# Get the directory of the script
$ScriptDirectory = Split-Path $ScriptPath

# Get the script name
# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
$ScriptName = $ScriptInvocation.MyCommand.Name

# Get the invocation path (relative to $PWD)
# @GregMac, this addresses your second point
$InvocationPath = ScriptInvocation.InvocationName

Por lo tanto, puede obtener la misma información que $ PSCommandPath, pero mucho más en el trato. No estoy seguro, pero parece que "Get-Variable" no estuvo disponible hasta PS3, por lo que no fue de mucha ayuda para sistemas realmente antiguos (no actualizados).

También hay algunos aspectos interesantes al usar "-Scope", ya que puede retroceder para obtener los nombres, etc. de las funciones que llaman. 0 = actual, 1 = padre, etc.

Espero que esto sea algo útil.

Ref, https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable

AntGut
fuente
1

Hice algunas pruebas con el siguiente script, tanto en PS 2 como en PS 4, y obtuve el mismo resultado. Espero que esto ayude a la gente.

$PSVersionTable.PSVersion
function PSscript {
  $PSscript = Get-Item $MyInvocation.ScriptName
  Return $PSscript
}
""
$PSscript = PSscript
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

""
$PSscript = Get-Item $MyInvocation.InvocationName
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

Resultados -

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1      

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts
Mark Crashley
fuente
1

Esto puede funcionar en la mayoría de las versiones de PowerShell:

(& { $MyInvocation.ScriptName; })

Esto puede funcionar para trabajos programados

Get-ScheduledJob |? Name -Match 'JOBNAMETAG' |% Command
improbable
fuente