powershell - Invocar un segundo script con argumentos de un script
parameter-passing command-line-arguments (7)
Soy relativamente nuevo en PowerShell y tengo un script que lee un archivo de configuración que da como resultado un conjunto de pares de valores de nombre que me gustaría pasar como argumentos a una función en un segundo script de PowerShell.
No sé qué parámetros se colocarán en este archivo de configuración en tiempo de diseño, así que justo en el momento en que necesito invocar este segundo script de PowerShell, básicamente solo tengo una variable que tiene la ruta a este segundo script, y un segundo variable que es una matriz de argumentos para pasar al script identificado en la variable de ruta.
Entonces la variable que contiene la ruta al segundo script ($ scriptPath), puede tener un valor como:
"c:/the/path/to/the/second/script.ps1"
La variable que contiene los argumentos ($ argumentList) podría ser algo así 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 cual se invoca este primer script.
He intentado 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 llamar a Start-Job de la siguiente manera:
Start-Job -FilePath $scriptPath -ArgumentList $argumentList
... pero esto falla con 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 establece su valor.
¿Qué me estoy perdiendo?
EDITAR:
Bien, aquí hay una maqueta del script que se llamará, 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í hay una maqueta del script de llamada, en un archivo llamado invokee.ps1:
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 ejecuto invoker.ps1, este es el error que estoy obteniendo en la primera llamada a Invoke-Expression:
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 usa argumentos cuyas rutas tienen espacios y la segunda no. ¿Estoy manejando mal la presencia de espacios en estos caminos?
Aha. Esto resultó ser un problema simple de espacios en el camino al script.
Cambiar la línea Expresión de Invocación a:
Invoke-Expression "& `"$scriptPath`" $argumentList"
... fue suficiente para que comience. ¡Gracias a Neolisk por su ayuda y comentarios!
Aquí hay una respuesta que cubre la cuestión más general de llamar a otro script PS de un script PS, como puede hacer si estuviera componiendo los scripts de muchos scripts pequeños y de propósito limitado.
Descubrí que se trataba simplemente de utilizar dot-sourcing. Es decir, simplemente lo haces:
# This is Script-A.ps1
. ./Script-B.ps1 -SomeObject $variableFromScriptA -SomeOtherParam 1234;
Encontré todas las preguntas y respuestas muy confusas y complicadas, y finalmente obtuve el método simple anterior, que es como llamar a otro guión como si fuera una función en el guión original, que me parece más intuitivo.
Dot-sourcing puede "importar" el otro script en su totalidad, usando:
. ./Script-B.ps1
Ahora es como si los dos archivos estuvieran fusionados.
En definitiva, lo que realmente me faltaba era la noción de que debería estar construyendo un módulo de funciones reutilizables.
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. Entonces, después de experimentar, mi opinión sobre el problema es esta:
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 debe asegurarse de que su script no tenga un parámetro "Script" o "ArgumentList".
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 olvide escapar de ellos `"$scriptPath`"
Podemos usar splatting para esto:
& $command @args
donde @args
( variable automática $ args ) se divide en una matriz de parámetros.
Bajo PS, 5.1
Puede ejecutarlo igual que la consulta SQL. primero, crea tu comando / Expresión y almacena en una variable y ejecuta / invoca.
$command = "./yourExternalScriptFile.ps1" + " -param1 ''$paramValue''"
Es bastante avanzado, no creo que necesite explicaciones. Así que todo configurado para ejecutar su comando ahora,
Invoke-Expression $command
Yo recomendaría atrapar la excepción aquí
Invoke-Expression
debería funcionar perfectamente, solo asegúrate de usarlo correctamente. Para su caso, debería verse así:
Invoke-Expression "$scriptPath $argumentList"
Probé este enfoque con Get-Service y parece estar funcionando como se esperaba.