Pytanie Wywołaj drugi skrypt z argumentami ze skryptu


Jestem stosunkowo 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 drugim skrypcie PowerShell.

Nie wiem, jakie parametry zostaną umieszczone w tym pliku konfiguracyjnym w czasie projektowania, więc w momencie, 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 drugą zmienna, która jest tablicą argumentów do przekazania do skryptu zidentyfikowanego w zmiennej ścieżki.

Zatem zmienna zawierająca ścieżkę do drugiego skryptu ($ scriptPath) może mieć wartość taką jak:

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

Zmienna zawierająca argumenty ($ argumentList) może wyglądać mniej więcej tak:

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

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

Chciałbym, aby wszystkie komendy write-host z tego drugiego skryptu były widoczne dla 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łatwiejszą pierwszą trasą było wypróbowanie Start-Job w następujący sposób:

Start-Job -FilePath $scriptPath -ArgumentList $argumentList

... ale to nie powiedzie się z tym błędem:

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 w 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 ustawiaj jego wartości.

czego mi brakuje?

EDYTOWAĆ:

Ok, oto makieta skryptu, który należy nazwać, 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

... a oto makieta 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 uruchamiam program invoker.ps1, jest to błąd, który aktualnie pojawia się przy pierwszym wywołaniu Wywołania-Wyrażenie:

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

Drugie wywołanie działa dobrze, ale istotną różnicą jest to, że pierwsza wersja używa argumentów, których ścieżki zawierają spacje, a druga nie. Czy niewłaściwie obchodzę obecność przestrzeni na tych ścieżkach?


44
2017-10-12 00:12


pochodzenie


Pomoże ci też, 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
Tak - próbuję odmalować mini wersję do zilustrowania. Wkrótce opublikujemy ... - Hoobajoob


Odpowiedzi:


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

Zmiana linii Wywołanie-Wyrażenie na:

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

... wystarczyło, żeby to się zaczęło. Dzięki Neolisk za pomoc i opinie!


41
2017-10-12 03:25



Cieszę się, że to rozwiązałeś. Jeśli moja odpowiedź była pomocna, możesz ją awansować. - Neolisk
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 jest definicją operatora połączenia. - Aeropher


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

Invoke-Expression "$scriptPath $argumentList"

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


38
2017-10-12 00:52



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
@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
Hm - Uruchomiłem ten drugi skrypt na wiele różnych sposobów, od plików wsadowych, IDE Visual Studio, 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
to będzie działać tylko wtedy, gdy obiekty na liście argumentów są ciągami wszystkich typów, mimo że prawda? - Backwards_Dave
@Backwards_Dave: $scriptPath i $argumentList tutaj są łańcuchy. Łańcuch może zawierać nie-łańcuchowy argument (zapisany jako ciąg). Mam nadzieję, że odpowie na twoje pytanie. - Neolisk


Dużo prostsze tak właściwie:

Metoda 1:

Invoke-Expression $scriptPath $argumentList

Metoda 2: 

& $scriptPath $argumentList

Metoda 3:

$scriptPath $argumentList

Jeśli masz spacje w swoim scriptPath, nie zapomnij ich uciec `"$scriptPath`"


23
2018-01-23 03:42



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


Oto odpowiedź obejmująca bardziej ogólną kwestię wywoływania innego skryptu PS ze skryptu PS, jak możesz zrobić, jeśli komponowałeś skrypty wielu małych, wąsko-celowych skryptów.

Zauważyłem, że był to po prostu przypadek użycia dot-sourcingu. To znaczy, po prostu:

# This is Script-A.ps1

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

Zauważyłem, że wszystkie pytania i odpowiedzi są bardzo mylące i skomplikowane, i ostatecznie wylądowałem na prostej powyższej metodzie, która tak naprawdę przypomina wywoływanie innego skryptu tak, jakby była funkcją w oryginalnym skrypcie, co wydaje mi się bardziej intuicyjne.

Dot-sourcing może "importować" drugi skrypt w całości, używając:

. ./Script-B.ps1

Teraz jest tak, jakby oba pliki zostały scalone.

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


6
2017-11-26 15:32





Możemy użyć splatting dla tego:

& $command @args

gdzie @args (zmienna automatyczna $ args) jest splatped na tablicy parametrów.

W PS, 5.1


3
2017-08-19 14:06





Próbowałem zaakceptowanego rozwiązania przy użyciu 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 eksperymentowaniu, moje podejście do problemu jest następujące:

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 twój skrypt nie ma parametru "Script" lub "ArgumentList".


2
2017-11-21 11:17





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

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

Jest całkiem do przodu, nie sądzę, że potrzebuje wyjaśnień. Więc wszystkie gotowe do wykonania polecenia teraz,

Invoke-Expression $command

Polecam złapać wyjątek tutaj


0
2017-08-03 10:19