web-dev-qa-db-fra.com

Invoquer un second script avec les arguments d'un script

Je suis relativement nouveau dans PowerShell et j’ai un script qui lit un fichier de configuration qui donne un ensemble de paires nom-valeur que je voudrais transmettre en tant qu’arguments à une fonction dans un second script PowerShell.

Je ne sais pas quels paramètres seront placés dans ce fichier de configuration au moment de la conception, donc juste au moment où je dois appeler ce second script PowerShell, je n'ai fondamentalement qu'une variable qui possède le chemin d'accès à ce second script, et une seconde. variable qui est un tableau d'arguments à transmettre au script identifié dans la variable de chemin.

Ainsi, la variable contenant le chemin d'accès au second script ($ scriptPath) pourrait avoir une valeur comme:

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

La variable contenant les arguments ($ argumentList) pourrait ressembler à ceci:

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

Comment puis-je passer de cet état de choses à l'exécution de script.ps1 avec tous les arguments de $ argumentList?

J'aimerais que toutes les commandes write-Host de ce second script soient visibles sur la console à partir de laquelle ce premier script est appelé.

J'ai essayé la dot-sourcing, Invoke-Command, Invoke-Expression et Start-Job, mais je n'ai pas trouvé d'approche qui ne génère pas d'erreurs.

Par exemple, je pensais que la première voie la plus facile consistait à essayer Start-Job, appelé comme suit:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList

... mais cela échoue avec cette erreur:

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

... dans ce cas, "ConfigFilename" est le premier paramètre de la liste des paramètres définie par le second script, et mon appel tente apparemment de définir sa valeur sur "-ConfigFilename", ce qui est évidemment destiné à identifier le paramètre par son nom. , pas définir sa valeur.

Qu'est-ce que je rate?

MODIFIER:

Ok, voici une maquette du script à appeler, dans un fichier nommé 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

... et voici une maquette du script d'appel dans un fichier nommé 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

Lorsque j'exécute invoker.ps1, c'est l'erreur que j'obtiens actuellement lors du premier appel à Invoke-Expression:

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

Le deuxième appel fonctionne très bien, mais une différence importante est que la première version utilise des arguments dont les chemins contiennent des espaces, et la seconde pas. Est-ce que je gère mal la présence d'espaces dans ces chemins?

50
Hoobajoob

Aha. Cela s'est avéré être un problème simple de la présence d'espaces dans le chemin du script.

Changer la ligne Invoke-Expression en:

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

... était suffisant pour le faire démarrer. Merci à Neolisk pour votre aide et vos commentaires!

45
Hoobajoob

Invoke-Expression devrait fonctionner parfaitement, assurez-vous de l’utiliser correctement. Pour votre cas, cela devrait ressembler à ceci:

Invoke-Expression "$scriptPath $argumentList"

J'ai testé cette approche avec Get-Service et semble fonctionner comme prévu.

41
Neolisk

Beaucoup plus simple en réalité:

Méthode 1:

Invoke-Expression $scriptPath $argumentList

Méthode 2: 

& $scriptPath $argumentList

Méthode 3:

$scriptPath $argumentList

Si vous avez des espaces dans votre scriptPath, n'oubliez pas de leur échapper `"$scriptPath`"

26
Mrchief

Voici une réponse à la question plus générale d'appeler un autre script PS à partir d'un script PS, comme vous le feriez si vous composiez vos scripts à partir de nombreux petits scripts à but étroit.

J'ai trouvé que c'était simplement un cas d'utilisation de la génération de points. C'est-à-dire que vous faites simplement:

# This is Script-A.ps1

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

J’ai trouvé le Q/A très déroutant et compliqué et j’ai finalement trouvé la méthode simple ci-dessus, qui revient à appeler un autre script comme si c’était une fonction du script original, ce qui me semble plus intuitif.

Dot-sourcing peut "importer" l'autre script dans son intégralité, en utilisant:

. ./Script-B.ps1

C'est maintenant comme si les deux fichiers étaient fusionnés.

En fin de compte, ce qui me manquait vraiment, c’est l’idée que je devrais créer un module de fonctions réutilisables.

8
Luke Puplett

Nous pouvons utiliser splatting pour cela:

& $command @args

@args ( variable automatique $ args ) est éclaboussé dans un tableau de paramètres. 

Sous PS, 5.1

5
Artyom

J'ai essayé la solution acceptée consistant à utiliser l'applet de commande Invoke-Expression, mais cela n'a pas fonctionné pour moi car mes arguments comportaient des espaces. J'ai essayé d'analyser les arguments et d'échapper aux espaces, mais je ne pouvais pas le faire fonctionner correctement et c'était vraiment un sale travail autour de moi, à mon avis .

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

Le seul inconvénient de cette solution est que vous devez vous assurer que votre script ne comporte pas de paramètre "Script" ou "ArgumentList".

2
gigi

Vous pouvez l'exécuter comme la requête SQL . Tout d'abord, construisez votre commande/Expression et stockez-le dans une variable, puis exécutez/invoquez.

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

C'est plutôt en avant, je ne pense pas qu'il ait besoin d'explications . Donc, prêt à exécuter votre commande maintenant,

Invoke-Expression $command

Je recommanderais d'attraper l'exception ici

0
UniCoder