41

Jestem względnie nowy w PowerShell i mam skrypt, który odczytuje plik konfiguracyjny, który daje zestaw par wartości nazw, które chciałbym przekazać jako argumenty do funkcji w drugi skrypt PowerShell.Inwokuj drugi skrypt z argumentami ze skryptu

Nie wiem, jakie parametry zostaną umieszczone w tym pliku konfiguracyjnym w czasie projektowania, więc w miejscu, w którym muszę wywołać ten drugi skrypt PowerShell, po prostu mam tylko jedną zmienną, która ma ścieżkę do tego drugiego skryptu i druga zmienna, która jest tablicą argumentów do przekazania do skryptu zidentyfikowanego w zmiennej ścieżki.

więc zmienna zawierająca ścieżkę do drugiego scenariusza ($ scriptpath), może mieć wartość jak:

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

zmienna zawierająca argumenty ($ argumentList) może wyglądać tak:

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

Jak przejść z tego stanu rzeczy do wykonywania script.ps1 z wszystkimi argumentami z $ argumentList?

Chciałbym, aby wszystkie komendy write-host z tego drugiego skryptu były widoczne na konsoli, z której wywoływany jest ten pierwszy skrypt.

Próbowałem dot-sourcing, Invoke-Command, Invoke-Expression i Start-Job, ale nie znalazłem podejście, które nie powoduje błędów.

Na przykład, myślałem, że najłatwiej Pierwsza trasa była próba Start-Job nazywa się następująco:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList 

... ale to nie z tego błędu:

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

... w tym przypadku "ConfigFilename" jest pierwszym parametrem na liście param zdefiniowanym przez drugi skrypt, a moje wywołanie najwyraźniej próbuje ustawić jego wartość na "-ConfigFilename", co oczywiście ma na celu zidentyfikowanie parametru po nazwie, nie jest ustawione jego wartość.

Czego mi brakuje?

EDIT:

Ok, o to mock-up z do-nazwie skryptu w pliku o nazwie 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 

... i tu jest mock-up skryptu wywołującego, w pliku o nazwie 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 

Kiedy biegnę invoker.ps1, jest to błąd jestem obecnie dostać się na pierwsze wezwanie do wywoływania ekspresji:

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

Drugie wywołanie działa dobrze, ale jedna istotna różnica jest taka, że ​​pierwsza wersja używa argumentów, których ścieżki mają spacje w nich, a drugi nie. Czy niewłaściwie obchodzę obecność przestrzeni na tych ścieżkach?

+0

Pomoże Ci również, jeśli podasz źródło dla każdego ze skryptów. Na przykład utwórz projekt POC (proof of concept) ilustrujący twój problem. Możemy przetestować i grać z nim, a może ktoś znajdzie rozwiązanie lub obejście problemu. – Neolisk

+0

Tak - próbuję odmalować mini wersję do zilustrowania. Wkrótce opublikuje ... – Hoobajoob

Odpowiedz

37

Aha. Okazało się, że jest to prosty problem polegający na tym, że w ścieżce do skryptu są spacje.

Zmiana linii Invoke-Expression do:

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

... wystarczyło, aby zmusić go do kick off. Dzięki Neolisk za pomoc i opinie!

+1

Cieszę się, że to rozwiązałeś. Jeśli moja odpowiedź była pomocna, możesz ją awansować. – Neolisk

+0

Aby dodać do swojej odpowiedzi. Posiadanie spacji na ścieżce oznacza, że ​​musisz używać cudzysłowów. Cytaty traktowane są jako ciąg, w przeciwieństwie do polecenia, przez Invoke-Expression. Dlatego jeśli chcesz wykonać łańcuch, musisz użyć "operatora połączenia", który jest symbolem ampersand na początku twojego przykładu. [Tutaj] (http://ss64.com/ps/call.html) jest definicją operatora połączenia. – Aeropher

37

Invoke-Expression powinien działać idealnie, tylko upewnij się, że używasz go poprawnie.Dla twojego przypadku powinno to wyglądać tak:

Invoke-Expression "$scriptPath $argumentList" 

Testowałem to podejście w usłudze Get-Service i wydaje się działać zgodnie z oczekiwaniami.

+0

Wywołanie Wyrażenie "$ scriptPath $ argumentList" zwraca natychmiast i nie wydaje się, aby uruchomić skrypt. Jeśli zmienię go na Wyrażenie wyrażeń "powershell $ scriptPath $ argumentList", PowerGui wydaje się zawieszać. – Hoobajoob

+2

@Hoobajoob: Invoke-Expression nie wraca natychmiast, cierpliwie czeka, aż zakończy się wywoływany skrypt - właśnie testowałem na projekcie POC. Musi być coś nie tak z skryptem, na który celujesz. – Neolisk

+0

Hm - Uruchomiłem ten drugi skrypt na wiele różnych sposobów, począwszy od plików wsadowych, Visual Studio IDE, linii poleceń DOS i sesji konsoli PowerShell, ale po raz pierwszy próbowałem uruchomić go z innego skryptu powłoki. Jakie rzeczy mogłyby się znaleźć w samym skrypcie, który uniemożliwiłby jego pomyślne uruchomienie za pośrednictwem Wywołania Invoke? – Hoobajoob

19

Znacznie simpler rzeczywiście:

Metoda 1:

Invoke-Expression $scriptPath $argumentList 

Metoda 2:

& $scriptPath $argumentList 

Metoda 3:

$scriptPath $argumentList 

Jeśli masz przestrzenie w swojej scriptPath, nie zapomnij, aby uciec im `"$scriptPath`"

+0

Dzięki za opcje, bardzo podoba mi się dodatkowa opcja dostarczona przez tę odpowiedź na inne pytanie. http://stackoverflow.com/a/33206405/3794873 – dragon788

5

Oto odpowiedź obejmujące bardziej ogólne pytanie o powołanie innego skryptu PS ze skryptu PS, jak można zrobić, jeśli były komponowanie skryptów wielu małych, wąsko-celowych skryptów.

Znalazłem, że był to po prostu przypadek użycia dot-sourcingu. Oznacza to, że po prostu zrobić:

# This is Script-A.ps1 

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

znalazłem cały Q/bardzo mylące i skomplikowane i ostatecznie wylądował na prostej metody powyżej, który jest naprawdę tak jak dzwoni inny skrypt tak, jakby to była funkcja w oryginalny skrypt, który wydaje mi się bardziej intuicyjny.

Dot-sourcing może „import” inny scenariusz w całości przy użyciu:

. ./Script-B.ps1 

To teraz jakby dwa pliki są połączone.

Ostatecznie, czego tak naprawdę mi brakowało, to koncepcja, że ​​powinienem budować moduł funkcji wielokrotnego użytku.

2

Próbowałem zaakceptowanego rozwiązania, używając polecenia cmdlet Invoke-Expression, ale nie działało ono dla mnie, ponieważ moje argumenty zawierały spacje. Próbowałem sparsować argumenty i uciec z przestrzeni, ale nie mogłem poprawnie sprawić, żeby działało, a także, moim zdaniem, była to naprawdę brudna robota. Więc po pewnym eksperymentowanie, moje podejście do problemu jest taka:

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 

Jedyną wadą tego rozwiązania jest to, że musisz upewnić się, że skrypt nie posiada „skrypt” lub „parametru” ArgumentList .

0

Można wykonać to samo, co zapytanie SQL. najpierw skompiluj polecenie/wyrażenie i zapisz w zmiennej i wykonaj/wywołaj.

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

Jest ładny, nie sądzę, że potrzebuje wyjaśnień. Więc wszystko ustawić, aby wykonać swoją komendę teraz

Invoke-Expression $command 

Polecam łowienie wyjątek tutaj