2012-10-02 13 views
7

Desidero creare una funzione di PowerShell che enumeri alcuni dati e generare un blocco di script su tutte le occorrenze.Funzione PowerShell con blocco di script parametrizzato

Ormai ho (questo non è il codice vero e proprio, ma illustra il mio problema):

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    process 
    { 
     $digits = 0..10 
     $digits | % { 
      $Action.Invoke($_); 
     } 
    } 
} 

ho messo questa funzione nel mio modulo. Tuttavia, non ottengo alcun risultato quando chiamo:

Invoke-TenTimes { $_ } 

L'output è vuoto (non viene visualizzato nulla).

Se chiamo

Invoke-TenTimes { $_ -eq $null } 

ottengo dieci true. In effetti, vedo che $_ è nullo.

Qual è il modo corretto di compilare il $_?

Che cosa sta facendo impazzire, è che se metto questa funzione e la chiamata nello stesso file PS1, funziona (ma voglio passare blocco di script su richiesta):

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    process 
    { 
     $digits = 0..10 
     $digits | % { 
      $Action.Invoke($_); 
     } 
    } 
} 


Invoke-TenTimes { $_ } 

risposta

2

questo è un problema scoping con il passato in scriptblock appartenente ad un ambito diverso da quello in cui è in esecuzione. Se lo script in passato non deve fare riferimento a variabili del suo contesto, puoi semplicemente clonare lo scriptblock per forzarlo a far parte dell'ambito in cui lo stai eseguendo. con

$action = [scriptblock]::Create($action) 

la cosa interessante è che ha funzionato in primo luogo solo da INCIDENTE. stava ottenendo $ _ dall'ambito genitore di cui apparteneva il blocco di script che si trovava all'interno di foreach-object che era buono.

comunque prendere la versione originale, e quindi eseguire questo

"gotcha" | % { 
Invoke-TenTimes { $_ } 
} 

e vedrete si otterrà Gotcha 10 volte, a causa $ _ non è nulla nel vostro ambito chiamanti, ma è qualcosa.

+0

Grazie! questo ha risolto il mio problema, e ora sono in grado di utilizzare un blocco di script con $ _ :). –

3

La questione è noto ed è specifico per i moduli. Forse questo è anche un bug. Si prega di dare un'occhiata a questa domanda: Strange behavior with Powershell scriptblock variable scope and modules, any suggestions?

Per quanto riguarda il tuo caso particolare, chiamare la funzione come questa:

Invoke-TenTimes { $args[0] } 

In questo modo dovrebbe funzionare come previsto.

+0

Questo ha risolto il mio problema. Ma aspetterò qualche giorno per vedere se qualcuno ha una soluzione reale da suggerire. –

5

Ecco come fare:

$scriptblock = {param($number) Write-Host "Current Number is $number"} 

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    $digits = 1..10 
    $digits | ForEach-Object{Invoke-Command -ScriptBlock $Action -Args $_} 
} 

Usa:

Invoke-TenTimes $scriptblock 
0

È MOLTO più facile di chiunque altro.

Considerare $ A e $ B sono definiti a livello globale e solo $ B è definito a livello di modulo. Il tuo blocco di script è un terzo ambito. All'interno di esso fai riferimento a $ A e ottieni il valore globale.Allo stesso modo se si fa riferimento a $ B si ottiene il valore del livello del modulo poiché oscura l'istanza globale.

PROBLEMA: se si tenta di scrivere su uno, viene creata un'istanza locale.

Risposta:

([ref]$A).value = 'Something malicious' 

nota che "([ref] $ {nome})" è un oggetto di riferimento in modo ".value" è necessario per raggiungere la variabile reale. Così

([ref]$B).value.Length 

è lo stesso di

$B.Length 

In entrambi i casi non si ha il controllo diretto sulle portata. Ottieni l'istanza da cui leggerei. Quindi il primo esempio modifica il Global $ A e il secondo esempio fa riferimento all'istanza del livello del modulo di $ B.

Problemi correlati