Llame a PowerShell script PS1 desde otro script PS1 dentro de Powershell ISE

138

Quiero ejecutar la llamada para un script myScript1.ps1 dentro de un segundo script myScript2.ps1 dentro de Powershell ISE.

El siguiente código dentro de MyScript2.ps1, funciona bien desde Powershell Administration, pero no funciona dentro de PowerShell ISE:

#Call myScript1 from myScript2
invoke-expression -Command .\myScript1.ps1

Obtengo el siguiente error cuando ejecuto MyScript2.ps1 de PowerShell ISE:

El término '. \ MyScript1.ps1' no se reconoce como el nombre de un cmdlet, función, archivo de script o programa operable. Verifique la ortografía del nombre, o si se incluyó una ruta, verifique que la ruta sea correcta e intente nuevamente.

Nicola Celiento
fuente

Respuestas:

83

Para encontrar la ubicación de un script, use Split-Path $MyInvocation.MyCommand.Path(asegúrese de usar esto en el contexto del script).

La razón por la que debe usar eso y nada más se puede ilustrar con este script de ejemplo.

## ScriptTest.ps1
Write-Host "InvocationName:" $MyInvocation.InvocationName
Write-Host "Path:" $MyInvocation.MyCommand.Path

Aquí hay algunos resultados.

PS C: \ Users \ JasonAr>. \ ScriptTest.ps1
InvocationName:. \ ScriptTest.ps1
Ruta: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Usuarios \ JasonAr>. . \ ScriptTest.ps1
InvocationName:.
Ruta: C: \ Users \ JasonAr \ ScriptTest.ps1

PS C: \ Users \ JasonAr> & ". \ ScriptTest.ps1"
InvocationName: &
Ruta: C: \ Users \ JasonAr \ ScriptTest.ps1

En PowerShell 3.0 y versiones posteriores, puede usar la variable automática $PSScriptRoot:

## ScriptTest.ps1
Write-Host "Script:" $PSCommandPath
Write-Host "Path:" $PSScriptRoot
PS C: \ Users \ jarcher>. \ ScriptTest.ps1
Script: C: \ Users \ jarcher \ ScriptTest.ps1
Ruta: C: \ Users \ jarcher
JasonMArcher
fuente
Una adición tardía: si le preocupa una variación abot (o, en realidad, solo quiere un código "sólido"), probablemente quiera usar "Write-Output" en lugar de "Write-Host".
KlaymenDK
20
Sería bueno ver un ejemplo de uso de Split-Path en la respuesta. También debe mostrar llamar a un script dentro de otro script realmente.
Jeremy
37

La ruta actual de MyScript1.ps1 no es la misma que myScript2.ps1. Puede obtener la ruta de la carpeta de MyScript2.ps1 y concatenarla a MyScript1.ps1 y luego ejecutarla. Ambas secuencias de comandos deben estar en la misma ubicación.

## MyScript2.ps1 ##
$ScriptPath = Split-Path $MyInvocation.InvocationName
& "$ScriptPath\MyScript1.ps1"
Shay Levy
fuente
¿Cómo tengo que inicializar la variable $ MyInvocation?
Nicola Celiento
No, es una variable automática.
Shay Levy
Funciona, pero obtenga el siguiente error antes de la ejecución real del script llamado: Split-Path: No se puede vincular el argumento al parámetro 'Path' porque es una cadena vacía. En la línea: 4 caracteres: 25 + $ ScriptPath = Split-Path <<<< $ MyInvocation.InvocationName + CategoryInfo: InvalidData: (:) [Split-Path], ParameterBindingValidationException + FullyQualifiedErrorId: ParameterArgumentValidationErrorEmptyStringNotAllowed.Command
Nicola Celiento
cree un nuevo script puesto: $ MyInvocation.InvocationName en él y ejecute el script. ¿Entiendes el camino del guión?
Shay Levy
@JasonMArcher - ¿Por qué en su lugar? Que yo sepa, ¿ambos dan la misma salida?
manojlds
36

Estoy llamando a myScript1.ps1 desde myScript2.ps1.

Suponiendo que ambos guiones estén en la misma ubicación, primero obtenga la ubicación del guión utilizando este comando:

$PSScriptRoot

Y, luego, agregue el nombre del script que desea llamar así:

& "$PSScriptRoot\myScript1.ps1"

Esto debería funcionar.

Srijani Ghosh
fuente
3
& "$PSScriptRoot\myScript1.ps1"es suficiente
Weihui Guo
19

Solución de una línea:

& ((Split-Path $MyInvocation.InvocationName) + "\MyScript1.ps1")
noelicus
fuente
Esto es bueno, pero ¿por qué no solo & '.\MyScript1.ps'si el script reside en el mismo directorio?
JoePC
3
Eso está usando el directorio actual, no el directorio de script. Seguro que a menudo son lo mismo ... ¡pero no siempre!
noelicus
9

Esto es solo información adicional a las respuestas para pasar argumentos al otro archivo

Donde esperas discusión

PrintName.ps1

Param(
    [Parameter( Mandatory = $true)]
    $printName = "Joe"    
)


Write-Host $printName

Cómo llamar al archivo

Param(
    [Parameter( Mandatory = $false)]
    $name = "Joe"    
)


& ((Split-Path $MyInvocation.InvocationName) + "\PrintName.ps1") -printName $name

Si no proporciona ninguna entrada, el valor predeterminado será "Joe" y se pasará como argumento al argumento printName en el archivo PrintName.ps1 que a su vez imprimirá la cadena "Joe"

cpoDesign
fuente
4

Es posible que ya haya encontrado la respuesta, pero esto es lo que hago.

Normalmente coloco esta línea al comienzo de mis scripts de instalación:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

Entonces puedo usar la variable $ PSScriptRoot como una ubicación del script actual (ruta), como en el siguiente ejemplo:

if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } #In case if $PSScriptRoot is empty (version of powershell V.2).  

Try {
If (Test-Path 'C:\Program Files (x86)') {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise64_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x64.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
} Else {
    $ChromeInstallArgs= "/i", "$PSScriptRoot\googlechromestandaloneenterprise_v.57.0.2987.110.msi", "/q", "/norestart", "/L*v `"C:\Windows\Logs\Google_Chrome_57.0.2987.110_Install_x86.log`""
    Start-Process -FilePath msiexec -ArgumentList $ChromeInstallArgs -Wait -ErrorAction Stop
    $Result= [System.Environment]::ExitCode
    }

} ### End Try block


Catch  {
    $Result = [System.Environment]::Exitcode
    [System.Environment]::Exit($Result)
   }
[System.Environment]::Exit($Result)

En su caso, puede reemplazar

Inicio-proceso ... línea con

Invoke-Expression $ PSScriptRoot \ ScriptName.ps1

Puede leer más sobre las variables automáticas $ MYINVOCATION y $ PSScriptRoot en el sitio de Microsoft: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_automatic_variables

Emil Bogopolskiy
fuente
4

Para ejecutar fácilmente un archivo de script en la misma carpeta (o subcarpeta de) que la persona que llama, puede usar esto:

# Get full path to the script:
$ScriptRoute = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, "Scriptname.ps1"))

# Execute script at location:
&"$ScriptRoute"
Rrr
fuente
3

Tuve un problema con esto. Sin embargo, no utilicé ninguna $MyInvocationcosa inteligente para solucionarlo. Si abre el ISE haciendo clic derecho en un archivo de script y seleccionando y editluego abre el segundo script desde el ISE, puede invocar uno desde el otro simplemente usando la sintaxis normal . \ Script.ps1 . Mi conjetura es que el ISE tiene la noción de una carpeta actual y abrirla así establece la carpeta actual en la carpeta que contiene los scripts. Cuando invoco un script de otro en uso normal, solo uso . \ Script.ps1 , en mi opinión, es incorrecto modificar el script solo para que funcione correctamente en el ISE ...

Sabroni
fuente
2

Tuve un problema similar y lo resolví de esta manera.

Mi directorio de trabajo es una carpeta de script general y una carpeta de script particular en la misma raíz, necesito llamar a la carpeta de script particular (que llama al script general con el parámetro del problema particular). Entonces el directorio de trabajo es así

\Nico\Scripts\Script1.ps1
             \Script2.ps1
      \Problem1\Solution1.ps1
               \ParameterForSolution1.config
      \Problem2\Solution2.ps1
               \ParameterForSolution2.config

Solutions1 y Solutions2 llaman a la PS1 en la carpeta Scripts cargando el parámetro almacenado en ParameterForSolution. Entonces, en powershell ISE ejecuto este comando

.\Nico\Problem1\Solution1.PS1

Y el código dentro de Solution1.PS1 es:

# This is the path where my script is running
$path = split-path -parent $MyInvocation.MyCommand.Definition

# Change to root dir
cd "$path\..\.."

$script = ".\Script\Script1.PS1"

$parametro = "Problem1\ParameterForSolution1.config"
# Another set of parameter Script1.PS1 can receive for debuggin porpuose
$parametro +=' -verbose'

Invoke-Expression "$script $parametro"
Nico Osorio
fuente
2

Presento mi ejemplo para su consideración. Así es como llamo un código de un script de controlador en las herramientas que hago. Los scripts que hacen el trabajo también necesitan aceptar parámetros, por lo que este ejemplo muestra cómo pasarlos. Asume que el script que se llama está en el mismo directorio que el script del controlador (script que realiza la llamada).

[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string[]]
$Computername,

[Parameter(Mandatory = $true)]
[DateTime]
$StartTime,

[Parameter(Mandatory = $true)]
[DateTime]
$EndTime
)

$ZAEventLogDataSplat = @{
    "Computername" = $Computername
    "StartTime"    = $StartTime
    "EndTime"      = $EndTime
}

& "$PSScriptRoot\Get-ZAEventLogData.ps1" @ZAEventLogDataSplat

Lo anterior es un script de controlador que acepta 3 parámetros. Estos se definen en el bloque param. El script del controlador luego llama al script llamado Get-ZAEventLogData.ps1. Por el bien de ejemplo, este script también acepta los mismos 3 parámetros. Cuando el script del controlador llama al script que hace el trabajo, debe llamarlo y pasar los parámetros. Lo anterior muestra cómo lo hago salpicando.

Zack A
fuente
1

¿Cómo ejecuta los scripts integrados de PowerShell dentro de sus scripts?

¿Cómo se utilizan los scripts integrados como

Get-Location
pwd
ls
dir
split-path
::etc...

Esos son ejecutados por su computadora, verificando automáticamente la ruta del script.

Del mismo modo, puedo ejecutar mis scripts personalizados simplemente poniendo el nombre del script en el bloque de script

::sid.ps1 is a PS script I made to find the SID of any user
::it takes one argument, that argument would be the username
echo $(sid.ps1 jowers)


(returns something like)> S-X-X-XXXXXXXX-XXXXXXXXXX-XXX-XXXX


$(sid.ps1 jowers).Replace("S","X")

(returns same as above but with X instead of S)

Vaya a la línea de comandos de PowerShell y escriba

> $profile

Esto devolverá la ruta a un archivo que nuestra línea de comandos de PowerShell ejecutará cada vez que abra la aplicación.

Se verá así

C:\Users\jowers\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

Vaya a Documentos y vea si ya tiene un directorio WindowsPowerShell. No lo hice

> cd \Users\jowers\Documents
> mkdir WindowsPowerShell
> cd WindowsPowerShell
> type file > Microsoft.PowerShellISE_profile.ps1

Ahora hemos creado el script que se lanzará cada vez que abramos la aplicación PowerShell.

La razón por la que lo hicimos fue para poder agregar nuestra propia carpeta que contiene todos nuestros scripts personalizados. Creemos esa carpeta y la llamaré "Bin" después de los directorios en los que Mac / Linux contiene sus scripts.

> mkdir \Users\jowers\Bin

Ahora queremos que ese directorio se agregue a nuestra $env:pathvariable cada vez que abramos la aplicación, así que regrese al WindowsPowerShellDirectorio y

> start Microsoft.PowerShellISE_profile.ps1

Luego agrega esto

$env:path += ";\Users\jowers\Bin"

Ahora el shell encontrará automáticamente sus comandos, siempre que guarde sus scripts en ese directorio "Bin".

Vuelva a iniciar el PowerShell y debería ser uno de los primeros scripts que se ejecutan.

Ejecute esto en la línea de comando después de volver a cargar para ver su nuevo directorio en su variable de ruta:

> $env:Path

Ahora podemos llamar a nuestros scripts desde la línea de comandos o desde otro script de la siguiente manera:

$(customScript.ps1 arg1 arg2 ...)

Como puede ver, debemos llamarlos con la .ps1extensión hasta que creemos alias para ellos. Si queremos ponernos elegantes.

Tyler Curtis Jowers
fuente
Wow, gracias por esto, hay mucho aquí. Pero ya hay otras 9 respuestas aquí. ¿Cómo difiere esto? ¿Qué información adicional proporciona?
Stephen Rauch el
Hacer esto nos permite usar nuestros scripts personalizados dentro de otros scripts, de la misma manera que los scripts incorporados se usan dentro de nuestros scripts. Haga esto y siempre que guarde sus scripts en el directorio que coloca en su ruta, la computadora encontrará automáticamente la ruta del script personalizado cuando lo use en la línea de comando o en otro script
Tyler Curtis Jowers