2013-03-20 10 views
6

Ho visto this question e this question ma non sono riuscito a trovare una soluzione al mio problema.Come chiamare una funzione di PowerShell all'interno dello script da Start-Job?

Quindi, ecco la situazione: Ho due funzioni DoWork e DisplayMessage in un file di script (.ps1). Ecco il codice:

### START OF SCRIPT ### 
function DoWork 
{ 
    $event = Register-EngineEvent -SourceIdentifier NewMessage -Action { 
     DisplayMessage($event.MessageData) 
    } 

    $scriptBlock = { 

    Register-EngineEvent -SourceIdentifier NewMessage -Forward 

    $message = "Starting work" 
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message 

    ### DO SOME WORK HERE ### 

    $message = "Ending work" 
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message 

    Unregister-Event -SourceIdentifier NewMessage 
    } 

    DisplayMessage("Processing Starts") 

    $array = @(1,2,3) 
    foreach ($a in $array) 
    { 
     Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array | Out-Null 
    } 

    #$jobs = Get-Job -Name "DoActualWork" 
    While (Get-Job -Name "DoActualWork" | where { $_.State -eq "Running" }) 
    { 
     Start-Sleep 1 
    } 

    DisplayMessage("Processing Ends") 

    Get-Job -Name "DoActualWork" | Receive-Job 
} 

function DisplayMessage([string]$message) 
{ 
    Write-Host $message -ForegroundColor Red 
} 

DoWork 

### END OF SCRIPT ### 

sto creando 3 processi in background (utilizzando $ array con 3 elementi) e l'utilizzo di eventi per passare i messaggi provenienti da processi in background per l'host. Mi aspetto che l'host di PowerShell visualizzi "Processing Starts" e "Processing Ends" 1 volta e "Starting work" e "Ending work" 3 volte ciascuno. Ma invece non ricevo "Starting work"/"Ending work" visualizzato in console.

evento viene trattato come un lavoro in PowerShell, così quando faccio Get-Job, posso vedere seguente errore associato al lavoro evento:

{Il termine 'DisplayMessage' non è riconosciuto come il nome di un cmdlet, funzione , file di script o programma eseguibile. Controllare l'ortografia del nome , o se un percorso è stato incluso, verificare che il percorso sia corretto e riprovare}

La mia domanda:. Come possiamo riutilizzare (o di riferimento) una funzione (DisplayMessage nel mio caso) definito nello stesso script da dove sto chiamando Start-Job? È possibile? So che possiamo usare -InitializationScript per passare funzioni/moduli a Start-Job, ma non voglio scrivere la funzione DisplayMessage due volte, una in script e un'altra in InitializationScript.

risposta

5

I processi in background vengono eseguiti in un processo separato, pertanto i lavori creati con Start-Job non possono interagire con le funzioni a meno che non vengano inclusi in $scriptblock.

Anche se hai incluso la funzione nel $scripblock, Write-Host non sarebbe uscita il suo contenuto alla console fino a quando si è utilizzato Get-Job | Receive-Job per ricevere i lavori risultato.

EDIT Il problema è che la funzione DisplayMessage è in uno script-ambito locale, mentre il tuo EventHandler viene eseguito in un diverso ambito genitore (come globale che è l'ambito di sessione), in modo che non riesce a trovare la vostra funzione. Se crei la funzione nell'ambito globale e la chiami dall'ambito globale, funzionerà.

Ho modificato lo script per farlo ora. Ho anche modificato per scriptblock e non registrati gli eventi quando lo script è fatto in modo non sarà possibile ottenere 10x messaggi quando si esegue lo script più volte :-)

Untitled1.ps1

function DoWork 
{ 
    $event = Register-EngineEvent -SourceIdentifier NewMessage -Action { 
     global:DisplayMessage $event.MessageData 
    } 

    $scriptBlock = { 

    Register-EngineEvent -SourceIdentifier NewMessage -Forward 

    $message = "Starting work $args" 
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message 

    ### DO SOME WORK HERE ### 

    $message = "Ending work $args" 
    $null = New-Event -SourceIdentifier NewMessage -MessageData $message 

    Unregister-Event -SourceIdentifier NewMessage 
    } 

    DisplayMessage("Processing Starts") 

    $array = @(1,2,3) 
    foreach ($a in $array) 
    { 
     Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $a | Out-Null 
    } 

    #$jobs = Get-Job -Name "DoActualWork" 
    While (Get-Job -Name "DoActualWork" | where { $_.State -eq "Running" }) 
    { 
     Start-Sleep 1 
    } 

    DisplayMessage("Processing Ends") 

    #Get-Job -Name "DoActualWork" | Receive-Job 
} 

function global:DisplayMessage([string]$message) 
{ 
    Write-Host $message -ForegroundColor Red 
} 

DoWork 

Get-EventSubscriber | Unregister-Event 

test

PS > .\Untitled1.ps1 
Processing Starts 
Starting work 1 
Starting work 2 
Ending work 1 
Ending work 2 
Starting work 3 
Ending work 3 
Processing Ends 
+0

Grazie. So che Start-Job non può interagire con le funzioni esterne, quindi queste domande. Inoltre, so che Write-Host non può essere inviato direttamente alla console, ecco perché sto usando l'evento per farlo.Nel mio caso, sono in grado di scrivere sull'host ma solo se uso "Write-Host" direttamente nell'evento ma non quando lo richiamo da una funzione. – digitguy

+0

Okey, credo di aver omesso la tua domanda, mi dispiace per quello. :) Guarda il nuovo contenuto alla fine della mia domanda. È il tuo problema attuale? –

+0

Sì, hai ragione. Il primo funziona, ma quest'ultimo no. – digitguy

6

un modo semplice per includere funzioni locali in un processo in background:

$init=[scriptblock]::create(@" 
function DoWork {$function:DoWork} 
"@) 

Start-Job -Name "DoActualWork" $ScriptBlock -ArgumentList $array -InitializationScript $init | Out-Null 
+0

Mi piace il tuo modo di includere le funzioni locali nel lavoro in background! Ma non ha risolto il mio problema (non sono sicuro del perché :(). Sono stato in grado di risolvere il mio problema usando la soluzione @Graimer sopra. – digitguy

+0

Ho fatto in modo che funzionasse usando le parentesi '{}' per creare il blocco di script invece di l'operatore '" @ @ "' dato che non interromperanno correttamente le variabili Esempio: '$ init = {function DoWork {$ function: DoWork}}' Quindi chiamalo allo stesso modo usando 'Start-job' &' - InitializationScript'. – Richard

+0

Ho ottenuto che la soluzione di mjolinor funzionasse, ma nel mio caso, includendo semplicemente 'DoWork' tramite l'argomento' init' non funzionava, avevo anche bisogno di includere qualsiasi funzione/variabile 'DoWork' referenziata, ecc, in modo ricorsivo. – Rob

Problemi correlati