41

Soy relativamente nuevo en PowerShell y tengo una secuencia de comandos que lee un archivo de configuración que da como resultado un conjunto de pares de nombre que me gustaría pasar como argumentos a una función en una segunda secuencia de comandos de PowerShell.Invocar una segunda secuencia de comandos con argumentos de una secuencia de comandos

No sé qué parámetros se colocarán en este archivo de configuración en tiempo de diseño, así que justo en el punto donde necesito invocar este segundo script de PowerShell, básicamente tengo una variable que tiene la ruta a este segundo script y una segunda variable que es una matriz de argumentos para pasar al script identificado en la variable de ruta.

Así que la variable que contiene la ruta de acceso al segundo guión ($ scriptPath), podría tener un valor como:

"c:\the\path\to\the\second\script.ps1" 

La variable que contiene los argumentos ($ ArgumentList) podría ser algo como:

-ConfigFilename "doohickey.txt" -RootDirectory "c:\some\kind\of\path" -Max 11 

¿Cómo puedo obtener de este estado de cosas la ejecución de script.ps1 con todos los argumentos de $ argumentList?

Me gustaría que cualquier comando de escritura de host de este segundo script sea visible para la consola desde la que se invoca este primer script.

He intentado con dot-sourcing, Invoke-Command, Invoke-Expression y Start-Job, pero no he encontrado un enfoque que no produzca errores.

Por ejemplo, pensé que la primera ruta más fácil era intentar Start-Job llamada de la siguiente manera:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList 

... pero esto produce este error:

System.Management.Automation.ValidationMetadataException: 
Attribute cannot be added because it would cause the variable 
ConfigFilename with value -ConfigFilename to become invalid. 

... en este caso, "ConfigFilename" es el primer parámetro en la lista de parámetros definida por el segundo script, y mi invocación aparentemente intenta establecer su valor en "-ConfigFilename", que obviamente está destinado a identificar el parámetro por nombre, no establecido es valioso.

¿Qué me estoy perdiendo?

EDIT:

Ok, aquí hay una maqueta de la secuencia de comandos a ser llamado, en un archivo llamado invokee.ps1

Param(
[parameter(Mandatory=$true)] 
[alias("rc")] 
[string] 
[ValidateScript({Test-Path $_ -PathType Leaf})] 
$ConfigurationFilename, 
[alias("e")] 
[switch] 
$Evaluate, 
[array] 
[Parameter(ValueFromRemainingArguments=$true)] 
$remaining) 

function sayHelloWorld() 
{ 
    Write-Host "Hello, everybody, the config file is <$ConfigurationFilename>." 
    if ($ExitOnErrors) 
    { 
     Write-Host "I should mention that I was told to evaluate things." 
    } 
    Write-Host "I currently live here: $gScriptDirectory" 
    Write-Host "My remaining arguments are: $remaining" 
    Set-Content .\hello.world.txt "It worked" 
} 

$gScriptPath = $MyInvocation.MyCommand.Path 
$gScriptDirectory = (Split-Path $gScriptPath -Parent) 
sayHelloWorld 

... y aquí es una maqueta de la secuencia de comandos de llamada, llamada en un invokee.ps1 archivo:

function pokeTheInvokee() 
{ 
    $scriptPath = (Join-Path -Path "." -ChildPath "invokee.ps1") 
    $scriptPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($scriptPath) 

    $configPath = (Join-Path -Path "." -ChildPath "invoker.ps1") 
    $configPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($configPath) 

    $argumentList = @() 
    $argumentList += ("-ConfigurationFilename", "`"$configPath`"") 
    $argumentList += , "-Evaluate" 

    Write-Host "Attempting to invoke-expression with: `"$scriptPath`" $argumentList" 
    Invoke-Expression "`"$scriptPath`" $argumentList" 
    Invoke-Expression ".\invokee.ps1 -ConfigurationFilename `".\invoker.ps1`" -Evaluate 
    Write-Host "Invokee invoked." 
} 

pokeTheInvokee 

Cuando corro invoker.ps1, este es el error que estoy recibiendo actualmente en la primera llamada a Invoke-Expresión:

Invoke-Expression : You must provide a value expression on 
the right-hand side of the '-' operator. 

La segunda llamada funciona bien, pero una diferencia significativa es que la primera versión utiliza argumentos cuyos caminos tienen espacios en ellos, y el segundo no lo hace. ¿Estoy manejando mal la presencia de espacios en estos caminos?

+0

También ayudaría si proporcionó la fuente para cada uno de sus scripts. Por ejemplo, cree un proyecto POC (prueba de concepto) que ilustre su problema. Luego podemos probarlo y jugar con él, y tal vez alguien encuentre una solución o una solución alternativa. – Neolisk

+0

Sí, estoy intentando hacer una versión mini para ilustrar. Se publicará pronto ... – Hoobajoob

Respuesta

37

Aha. Esto resultó ser un problema simple de espacios en el camino al script.

Cambio de la línea de Invoke-Expression para:

Invoke-Expression "& `"$scriptPath`" $argumentList" 

... fue suficiente para conseguir que se Inicial. ¡Gracias a Neolisk por su ayuda y comentarios!

+1

Me alegra que lo hayas resuelto. Si mi respuesta fue útil, puede votarlo mejor. – Neolisk

+0

Para agregar a tu respuesta. Tener espacios en el camino significa que tienes que usar comillas. Las comillas se tratan como una cadena, en oposición a un comando, por Invocación-Expresión. Por lo tanto, si desea ejecutar la cadena, debe usar el "operador de llamada", que es el símbolo comercial al comienzo de su ejemplo. [Aquí] (http://ss64.com/ps/call.html) es una definición para el operador de llamada. – Aeropher

37

Invoke-Expression debe funcionar perfectamente, solo asegúrese de estar utilizándolo correctamente.Para su caso, debería tener este aspecto:

Invoke-Expression "$scriptPath $argumentList" 

He probado este método con Get-Servicio y parece estar funcionando como se esperaba.

+0

Invoke-Expression "$ scriptPath $ argumentList" se devuelve inmediatamente y no parece ejecutar el script. Si lo cambio a Invoke-Expression "powershell $ scriptPath $ argumentList", PowerGui simplemente se cuelga. – Hoobajoob

+2

@Hoobajoob: Invoke-Expression no regresa inmediatamente, espera pacientemente hasta que finaliza el guión invocado: acabo de probar en un proyecto POC. Debe haber algo mal con el script al que apunta. – Neolisk

+0

Hm - He ejecutado este segundo script de diferentes formas desde los archivos por lotes, el IDE del estudio visual, las líneas de comando de DOS y las sesiones de consola de powershell, pero esta es la primera vez que intento ejecutarlo desde otro script de powershell. ¿Qué tipo de cosas podría haber en el guión mismo que impidan que se inicie con éxito a través de Invoke-Expression? – Hoobajoob

19

Mucho simpler realidad:

Método 1:

Invoke-Expression $scriptPath $argumentList 

Método 2:

& $scriptPath $argumentList 

Método 3:

$scriptPath $argumentList 

Si tiene espacios en su scriptPath, no se olvide de escapar de ellos `"$scriptPath`"

+0

Gracias por las opciones, realmente me gusta la opción adicional proporcionada por esta respuesta en otra pregunta. http://stackoverflow.com/a/33206405/3794873 – dragon788

5

Aquí es una respuesta que cubre la cuestión más general de llamar a otro script PS partir de un guión PS, ya que puede hacer si usted fuera componiendo tus scripts de muchos pequeños scripts de propósito limitado.

Encontré que era simplemente una cuestión de usar dot-sourcing. Es decir, que acaba de hacer:

# This is Script-A.ps1 

. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234; 

me encontré con todos los Q/A muy confuso y complicado y, finalmente, aterrizó en el método anterior simple, que en realidad es como llamar a otro guión como si fuera una función en el guión original, que me parece más intuitivo.

de puntos de abastecimiento puede "importar" la otra secuencia de comandos en su totalidad, utilizando:

. ./Script-B.ps1 

Ahora es como si se combinan los dos archivos.

En última instancia, lo que realmente me faltaba era la noción de que debería estar construyendo un módulo de funciones reutilizables.

2

Intenté la solución aceptada de usar el cmdlet Invoke-Expression pero no funcionó porque mis argumentos tenían espacios en ellos. Traté de analizar los argumentos y escapar de los espacios, pero no pude hacerlo funcionar correctamente y, en mi opinión, fue realmente un trabajo sucio. Así que después de algunos experimentos, mi opinión sobre el problema es el siguiente:

function Invoke-Script 
{ 
    param 
    (
     [Parameter(Mandatory = $true)] 
     [string] 
     $Script, 

     [Parameter(Mandatory = $false)] 
     [object[]] 
     $ArgumentList 
    ) 

    $ScriptBlock = [Scriptblock]::Create((Get-Content $Script -Raw)) 
    Invoke-Command -NoNewScope -ArgumentList $ArgumentList -ScriptBlock $ScriptBlock -Verbose 
} 

# example usage 
Invoke-Script $scriptPath $argumentList 

El único inconveniente de esta solución es que se necesita para asegurarse de que su guión no tiene un parámetro o "escritura" del "ArgumentList" .

0

Puede ejecutarlo igual que la consulta SQL. primero, construye tu comando/Expresión y almacena en una variable y ejecuta/invoca.

$command = ".\yourExternalScriptFile.ps1" + " -param1 '$paramValue'" 

Es bastante directo, no creo que necesite explicaciones. Así todo listo para ejecutar su comando ahora,

Invoke-Expression $command 

lo recomiendo agarrar la excepción aquí

Cuestiones relacionadas